diff --git a/api/composer.json b/api/composer.json index 1e9cf1fa..bc63c185 100644 --- a/api/composer.json +++ b/api/composer.json @@ -3,16 +3,17 @@ "description": "API Multi-sites", "type": "project", "require": { - "php": ">=8.1", - "phpmailer/phpmailer": "^6.8", - "ext-pdo": "*", + "php": ">=8.3", + "ext-json": "*", "ext-openssl": "*", - "ext-json": "*" + "ext-pdo": "*", + "phpmailer/phpmailer": "^6.8", + "phpoffice/phpspreadsheet": "^2.0" }, "autoload": { - "psr-4": { - "App\\": "src/" - } + "classmap": [ + "src/" + ] }, "config": { "sort-packages": true, diff --git a/api/composer.lock b/api/composer.lock index be981f46..8aad9bae 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,20 +4,284 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "03e608fa83a14a82b3f9223977e9674e", + "content-hash": "cf5e9de2a9687d04e4e094ad368ce366", "packages": [ { - "name": "phpmailer/phpmailer", - "version": "v6.9.3", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "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": "maennchen/zipstream-php", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.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.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-01-27T12:07:53+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": "phpmailer/phpmailer", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", "shasum": "" }, "require": { @@ -77,7 +341,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" }, "funding": [ { @@ -85,7 +349,323 @@ "type": "github" } ], - "time": "2024-11-24T18:04:13+00:00" + "time": "2025-04-24T15:19:31+00:00" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "7a700683743bf1c4a21837c84b266916f1aa7d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/7a700683743bf1c4a21837c84b266916f1aa7d25", + "reference": "7a700683743bf1c4a21837c84b266916f1aa7d25", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "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/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.6 || ^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", + "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" + } + ], + "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/2.3.8" + }, + "time": "2025-02-08T03:01:45+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+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" } ], "packages-dev": [], @@ -95,10 +675,10 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.1", - "ext-pdo": "*", + "php": ">=8.3", + "ext-json": "*", "ext-openssl": "*", - "ext-json": "*" + "ext-pdo": "*" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/api/deploy-api.sh b/api/deploy-api.sh index b70a6761..825e75de 100755 --- a/api/deploy-api.sh +++ b/api/deploy-api.sh @@ -128,6 +128,12 @@ $SSH_JUMP_CMD " incus exec ${INCUS_CONTAINER} -- chmod -R 775 ${FINAL_PATH}/logs || exit 1 incus exec ${INCUS_CONTAINER} -- find ${FINAL_PATH}/logs -type f -exec chmod 664 {} \; || exit 1 + echo '📁 Création des dossiers uploads...' + incus exec ${INCUS_CONTAINER} -- mkdir -p ${FINAL_PATH}/uploads || exit 1 + incus exec ${INCUS_CONTAINER} -- chown -R ${FINAL_OWNER}:${FINAL_OWNER_LOGS} ${FINAL_PATH}/uploads || exit 1 + incus exec ${INCUS_CONTAINER} -- chmod -R 775 ${FINAL_PATH}/uploads || exit 1 + incus exec ${INCUS_CONTAINER} -- find ${FINAL_PATH}/uploads -type f -exec chmod -R 664 {} \; || exit 1 + echo '🧹 Nettoyage...' incus exec ${INCUS_CONTAINER} -- rm -f /tmp/${ARCHIVE_NAME} || exit 1 rm -f /tmp/${ARCHIVE_NAME} || exit 1 diff --git a/api/docs/EXPORT-SYSTEM.md b/api/docs/EXPORT-SYSTEM.md new file mode 100644 index 00000000..5f9134b5 --- /dev/null +++ b/api/docs/EXPORT-SYSTEM.md @@ -0,0 +1,276 @@ +# Système d'Export/Import d'Opérations - Geosector API + +## Vue d'ensemble + +Le système d'export/import permet de sauvegarder et restaurer des opérations complètes avec toutes leurs données associées (passages, utilisateurs, secteurs, relations). + +## Architecture + +### Routes API + +#### Exports + +- `GET /api/operations/{id}/export/excel` - Export Excel (consultation) +- `GET /api/operations/{id}/export/json` - Export JSON (sauvegarde) +- `GET /api/operations/{id}/export/full` - Export combiné (Excel + JSON) + +#### Gestion des sauvegardes + +- `GET /api/operations/{id}/backups` - Liste des sauvegardes +- `GET /api/operations/{id}/backups/{backup_id}` - Télécharger une sauvegarde +- `DELETE /api/operations/{id}/backups/{backup_id}` - Supprimer une sauvegarde + +### Structure des fichiers + +``` +uploads/entites/{entite_id}/operations/{operation_id}/documents/exports/ +├── excel/ +│ └── geosector-export-{operation_id}-{timestamp}.xlsx +└── json/ + └── geosector-backup-{operation_id}-{type}-{timestamp}.json +``` + +## Export Excel + +### Contenu + +Le fichier Excel contient 4 feuilles : + +#### 1. Feuille "Passages" + +- **Colonnes** : ID_Passage, Date, Heure, Prénom, Nom, Tournée, Type, N°, Rue, Ville, Habitat, Donateur, Email, Tél, Montant, Règlement, Remarque, FK_User, FK_Sector, FK_Operation +- **Données déchiffrées** : Noms, emails, téléphones +- **Formatage** : Dates françaises (dd/mm/yyyy), types traduits + +#### 2. Feuille "Utilisateurs" + +- **Colonnes** : ID_User, Nom, Prénom, Email, Téléphone, Mobile, Rôle, Date_création, Actif, FK_Entite +- **Données déchiffrées** : Informations personnelles + +#### 3. Feuille "Secteurs" + +- **Colonnes** : ID_Sector, Libellé, Couleur, Date_création, Actif, FK_Operation + +#### 4. Feuille "Secteurs-Utilisateurs" + +- **Colonnes** : ID_Relation, FK_Sector, Nom_Secteur, FK_User, Nom_Utilisateur, Date_assignation, FK_Operation + +### Paramètres optionnels + +- `?user_id={id}` - Filtrer les passages par utilisateur + +### Exemple d'utilisation + +```bash +# Export complet +GET /api/operations/2644/export/excel + +# Export filtré par utilisateur +GET /api/operations/2644/export/excel?user_id=123 +``` + +## Export JSON + +### Structure du fichier JSON + +```json +{ + "export_metadata": { + "version": "1.0", + "export_date": "2025-06-21T16:19:23Z", + "source_entite_id": 5, + "export_type": "full_operation" + }, + "operation": { + "id": 2644, + "libelle": "OPE 2024-25", + "date_deb": "2024-09-01", + "date_fin": "2025-05-30", + "fk_entite": 5, + "chk_distinct_sectors": 1, + "created_at": "2024-08-15T10:00:00Z" + }, + "users": [...], + "sectors": [...], + "passages": [...], + "user_sectors": [...] +} +``` + +### Types d'export JSON + +- **manual** : Export à la demande (par défaut) +- **auto** : Sauvegarde automatique (avant modifications importantes) + +### Paramètres + +- `?type=manual|auto` - Type d'export + +## Sécurité + +### Contrôles d'accès + +- ✅ Authentification obligatoire +- ✅ Vérification d'appartenance à l'entité +- ✅ Isolation des données par entité +- ✅ Logs détaillés de toutes les opérations + +### Données sensibles + +- ✅ Chiffrement/déchiffrement automatique +- ✅ Données personnelles protégées +- ✅ Pas d'exposition des clés de chiffrement + +## Stockage et organisation + +### Enregistrement en base + +Tous les fichiers sont enregistrés dans la table `medias` : + +```sql +support = 'operation' +support_id = {operation_id} +file_type = 'xlsx' | 'json' +description = 'Export Excel opération - {libelle}' +``` + +### Métadonnées des fichiers + +- **ID** : Identifiant unique en base +- **Filename** : Nom du fichier généré +- **Path** : Chemin relatif depuis la racine +- **Size** : Taille en octets +- **Type** : excel | json + +## Exemples de réponses API + +### Export Excel réussi + +```json +{ + "status": "success", + "message": "Export Excel généré avec succès", + "file": { + "id": 123, + "filename": "geosector-export-2644-20250621-161923.xlsx", + "path": "uploads/entites/5/operations/2644/documents/exports/excel/geosector-export-2644-20250621-161923.xlsx", + "size": 45678, + "type": "excel" + } +} +``` + +### Export complet réussi + +```json +{ + "status": "success", + "message": "Export complet généré avec succès", + "files": { + "excel": { + "id": 123, + "filename": "geosector-export-2644-20250621-161923.xlsx", + "path": "uploads/entites/5/operations/2644/documents/exports/excel/geosector-export-2644-20250621-161923.xlsx", + "size": 45678, + "type": "excel" + }, + "json": { + "id": 124, + "filename": "geosector-backup-2644-manual-20250621-161923.json", + "path": "uploads/entites/5/operations/2644/documents/exports/json/geosector-backup-2644-manual-20250621-161923.json", + "size": 12345, + "type": "json" + } + } +} +``` + +### Liste des sauvegardes + +```json +{ + "status": "success", + "backups": [ + { + "id": 124, + "fichier": "geosector-backup-2644-manual-20250621-161923.json", + "file_type": "json", + "file_size": 12345, + "description": "Sauvegarde JSON opération - manual - OPE 2024-25", + "created_at": "2025-06-21 16:19:23", + "fk_user_creat": 1 + }, + { + "id": 123, + "fichier": "geosector-export-2644-20250621-161923.xlsx", + "file_type": "xlsx", + "file_size": 45678, + "description": "Export Excel opération - OPE 2024-25", + "created_at": "2025-06-21 16:19:23", + "fk_user_creat": 1 + } + ] +} +``` + +## Installation et dépendances + +### PhpSpreadsheet + +```bash +composer require phpoffice/phpspreadsheet +``` + +### Permissions de dossiers + +```bash +chmod 755 uploads/ +chmod 755 uploads/entites/ +``` + +## Gestion des erreurs + +### Erreurs courantes + +- **401** : Non authentifié +- **403** : Pas d'accès à l'entité +- **404** : Opération non trouvée +- **500** : Erreur de génération + +### Logs + +Tous les événements sont loggés via `LogService` : + +- Exports réussis (level: info) +- Erreurs de génération (level: error) +- Tentatives d'accès non autorisées (level: warning) + +## Maintenance + +### Nettoyage automatique (à implémenter) + +- Sauvegardes auto > 30 jours +- Fichiers temporaires > 24h +- Vérification cohérence base/fichiers + +### Monitoring + +- Espace disque utilisé +- Nombre de fichiers par entité +- Fréquence des exports + +## Évolutions futures + +### Import/Restauration + +- Validation des fichiers JSON +- Import transactionnel +- Gestion des conflits d'IDs +- Mapping entités source/cible + +### Optimisations + +- Compression des fichiers +- Export asynchrone pour gros volumes +- Cache des exports fréquents +- API de streaming pour téléchargements diff --git a/api/docs/FILE-SYSTEM-API.md b/api/docs/FILE-SYSTEM-API.md new file mode 100644 index 00000000..a8b155cf --- /dev/null +++ b/api/docs/FILE-SYSTEM-API.md @@ -0,0 +1,376 @@ +# API de Gestion des Fichiers - Geosector + +## Vue d'ensemble + +L'API de gestion des fichiers permet aux administrateurs de naviguer, rechercher et gérer les fichiers stockés dans l'application Geosector avec des contrôles d'accès basés sur les rôles. + +## Contrôles d'accès + +### Rôle 2 (Admin d'entité) + +- Accès limité aux fichiers de son entité uniquement +- Chemin racine : `/uploads/entites/{son_entite_id}/` +- Peut naviguer dans tous les sous-dossiers de son entité + +### Rôle > 2 (Super admin) + +- Accès complet à tous les fichiers +- Chemin racine : `/uploads/` (accès total) +- Peut naviguer dans toutes les entités et dossiers système + +## Routes disponibles + +### Navigation et listing + +#### `GET /api/files/browse` + +Navigation dans l'arborescence avec recherche et pagination. + +**Paramètres de requête :** + +- `path` (string) : Chemin à explorer (ex: `entites/5/operations`) +- `page` (int) : Page (défaut: 1) +- `per_page` (int) : Éléments par page (défaut: 50, max: 100) +- `search` (string) : Recherche dans nom, nom original, description +- `type` (string) : Filtrage par extension (pdf, jpg, xlsx, etc.) +- `category` (string) : Filtrage par catégorie métier +- `sort` (string) : Tri (name, date, size, type) - défaut: date +- `order` (string) : Ordre (asc, desc) - défaut: desc + +**Exemple :** + +```bash +GET /api/files/browse?path=entites/5/operations&search=2024&type=xlsx&page=1 +``` + +**Réponse :** + +```json +{ + "status": "success", + "current_path": "entites/5/operations", + "parent_path": "entites/5", + "pagination": { + "current_page": 1, + "per_page": 50, + "total_items": 127, + "total_pages": 3, + "has_next": true, + "has_prev": false + }, + "filters": { + "search": "2024", + "type": "xlsx", + "category": null, + "sort": "date", + "order": "desc" + }, + "files": [ + { + "id": 123, + "fichier": "planning_2024_op2644.xlsx", + "original_name": "Planning Opération 2024.xlsx", + "file_type": "xlsx", + "file_category": "planning", + "description": "Planning détaillé opération 2024", + "file_size": 1024000, + "file_path": "entites/5/operations/2644/documents/planning_2024_op2644.xlsx", + "created_at": "2025-06-22 08:30:00", + "creator_name": "Jean Dupont" + } + ], + "summary": { + "total_files": 45, + "total_size": 25600000, + "by_category": { + "planning": 12, + "export": 20, + "backup": 13 + } + } +} +``` + +#### `GET /api/files/list/{support}/{id}` + +Liste des fichiers par support (entite, user, operation, passage). + +**Paramètres :** + +- `support` : Type de support (entite, user, operation, passage) +- `id` : ID de l'élément +- Mêmes paramètres de requête que `/browse` + +**Exemple :** + +```bash +GET /api/files/list/operation/2644?category=export&page=1 +``` + +### Recherche + +#### `GET /api/files/search` + +Recherche globale dans tous les fichiers accessibles. + +**Paramètres de requête :** + +- `q` (string, requis) : Terme de recherche +- `page`, `per_page`, `type`, `category`, `sort`, `order` : Mêmes que browse + +**Exemple :** + +```bash +GET /api/files/search?q=planning&type=xlsx&category=planning +``` + +### Actions sur fichiers + +#### `GET /api/files/download/{id}` + +Téléchargement sécurisé d'un fichier. + +**Réponse :** Fichier en téléchargement direct avec headers appropriés. + +#### `DELETE /api/files/{id}` + +Suppression sécurisée d'un fichier (physique + base de données). + +**Réponse :** + +```json +{ + "status": "success", + "message": "Fichier supprimé avec succès" +} +``` + +#### `GET /api/files/info/{id}` + +Informations détaillées d'un fichier. + +**Réponse :** + +```json +{ + "status": "success", + "file": { + "id": 123, + "fichier": "planning_2024.xlsx", + "original_name": "Planning Opération 2024.xlsx", + "file_type": "xlsx", + "file_category": "planning", + "file_size": 1024000, + "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "description": "Planning détaillé", + "support": "operation", + "support_id": 2644, + "fk_entite": 5, + "created_at": "2025-06-22 08:30:00", + "updated_at": "2025-06-22 08:30:00", + "creator_name": "Jean Dupont", + "modifier_name": null, + "file_exists": true + } +} +``` + +### Statistiques + +#### `GET /api/files/stats` + +Statistiques d'utilisation des fichiers. + +**Pour admin d'entité (rôle 2) :** + +```json +{ + "status": "success", + "entite_id": 5, + "storage": { + "total_files": 245, + "total_size": 157286400, + "by_support": { + "entite": { "count": 12, "size": 45000000 }, + "operation": { "count": 180, "size": 98000000 }, + "user": { "count": 45, "size": 12000000 }, + "passage": { "count": 8, "size": 2286400 } + }, + "by_category": { + "document": 25, + "export": 120, + "avatar": 45, + "photo": 55 + }, + "by_type": { + "xlsx": 85, + "jpg": 120, + "pdf": 40 + } + } +} +``` + +**Pour super admin (rôle > 2) :** + +```json +{ + "status": "success", + "global_stats": { + "total_files": 2450, + "total_size": 1572864000, + "entites_count": 25, + "by_entite": [ + { "entite_id": 5, "files": 245, "size": 157286400 }, + { "entite_id": 12, "files": 180, "size": 98000000 } + ] + } +} +``` + +### Métadonnées + +#### `GET /api/files/metadata` + +Informations sur les catégories, extensions et limites autorisées. + +**Réponse :** + +```json +{ + "status": "success", + "categories": { + "entite": ["logo", "document", "reglement", "statut"], + "user": ["avatar", "photo"], + "operation": ["planning", "liste", "export", "backup"], + "passage": ["recu", "photo", "justificatif", "carte"] + }, + "extensions": ["pdf", "jpg", "jpeg", "png", "gif", "webp", "xlsx", "xls", "json", "csv"], + "mime_types": { + "pdf": "application/pdf", + "jpg": "image/jpeg", + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + }, + "max_file_sizes": { + "entite": 20971520, // 20 MB + "user": 5242880, // 5 MB + "operation": 20971520, // 20 MB + "passage": 10485760 // 10 MB + } +} +``` + +## Catégories de fichiers + +### Distinction Extension vs Catégorie + +- **Extension** (`file_type`) : Type technique (pdf, jpg, xlsx, png, etc.) +- **Catégorie** (`file_category`) : Type métier (logo, carte, photo, document, planning, etc.) + +### Catégories par support + +#### Entité + +- `logo` : Logo de l'entité +- `document` : Documents généraux +- `reglement` : Règlements internes +- `statut` : Statuts de l'entité + +#### Utilisateur + +- `avatar` : Photo de profil +- `photo` : Photos diverses + +#### Opération + +- `planning` : Plannings d'opération +- `liste` : Listes diverses +- `export` : Exports de données +- `backup` : Sauvegardes automatiques + +#### Passage + +- `recu` : Reçus de passage +- `photo` : Photos de passage +- `justificatif` : Justificatifs divers +- `carte` : Cartes et plans + +## Sécurité + +### Validation des chemins + +- Empêche les traversées de répertoire (`../`) +- Validation stricte selon le rôle utilisateur +- Contrôle d'accès au niveau fichier + +### Logs + +- Tous les téléchargements sont loggés +- Toutes les suppressions sont tracées +- Erreurs d'accès enregistrées + +### Contrôles d'intégrité + +- Vérification de l'existence physique des fichiers +- Validation des permissions avant chaque action +- Contrôle de cohérence base/fichiers + +## Exemples d'utilisation + +### Navigation dans les opérations d'une entité + +```bash +GET /api/files/browse?path=entites/5/operations&sort=name&order=asc +``` + +### Recherche de tous les exports Excel + +```bash +GET /api/files/search?q=export&type=xlsx&category=export +``` + +### Statistiques de stockage + +```bash +GET /api/files/stats +``` + +### Téléchargement d'un fichier + +```bash +GET /api/files/download/123 +``` + +### Suppression d'un fichier + +```bash +DELETE /api/files/123 +``` + +## Codes d'erreur + +- **401** : Non authentifié +- **403** : Accès refusé (rôle insuffisant ou fichier d'une autre entité) +- **404** : Fichier ou chemin non trouvé +- **400** : Paramètres invalides (terme de recherche manquant, etc.) +- **500** : Erreur serveur + +## Migration base de données + +Pour utiliser le système, exécuter la migration : + +```sql +-- Ajout de la colonne file_category +ALTER TABLE `medias` +ADD COLUMN `file_category` varchar(50) DEFAULT NULL COMMENT 'Catégorie du fichier (logo, carte, photo, document, etc.)' AFTER `file_type`; + +-- Index pour optimiser les requêtes +ALTER TABLE `medias` +ADD INDEX `idx_file_category` (`file_category`); +``` + +--- + +**Version** : 1.0 +**Date** : Juin 2025 +**Auteur** : API Geosector Team diff --git a/api/docs/README-UPLOAD.md b/api/docs/README-UPLOAD.md new file mode 100644 index 00000000..981a5c3a --- /dev/null +++ b/api/docs/README-UPLOAD.md @@ -0,0 +1,339 @@ +# Système de Gestion des Fichiers - API Geosector + +## Vue d'ensemble + +Ce document décrit l'organisation et la gestion des fichiers uploadés dans l'API Geosector. Le système permet de stocker et organiser différents types de fichiers par entité, utilisateur, opération et passage. + +## Structure des Dossiers + +``` +uploads/ +├── entites/ +│ ├── {entite_id}/ +│ │ ├── documents/ # PDF, Excel généraux de l'entité +│ │ ├── images/ # Images de l'entité +│ │ ├── users/ # Dossier pour les fichiers des utilisateurs +│ │ │ └── {user_id}/ # Images par utilisateur (avatars, etc.) +│ │ └── operations/ # Dossier pour les opérations +│ │ └── {operation_id}/ +│ │ ├── documents/ # Fichiers Excel de l'opération +│ │ └── passages/ # Fichiers des passages de cette opération +│ │ └── {passage_id}/ # PDF et images par passage +│ └── temp/ # Fichiers temporaires avant validation +``` + +### Exemples de chemins + +- Document d'entité : `uploads/entites/5/documents/reglement_2024.pdf` +- Avatar utilisateur : `uploads/entites/5/users/123/avatar.jpg` +- Excel d'opération : `uploads/entites/5/operations/2644/documents/planning.xlsx` +- Photo de passage : `uploads/entites/5/operations/2644/passages/789/photo_1.jpg` + +## Structure de la Table `medias` + +### Table existante enrichie + +```sql +-- Structure complète de la table medias +CREATE TABLE `medias` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `support` varchar(45) NOT NULL DEFAULT '' COMMENT 'Type de support (entite, user, operation, passage)', + `support_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'ID de l\'élément associé', + `fichier` varchar(250) NOT NULL DEFAULT '' COMMENT 'Nom du fichier stocké', + `file_type` varchar(50) DEFAULT NULL COMMENT 'Extension du fichier (pdf, jpg, xlsx, etc.)', + `file_size` int(10) unsigned DEFAULT NULL COMMENT 'Taille du fichier en octets', + `mime_type` varchar(100) DEFAULT NULL COMMENT 'Type MIME du fichier', + `original_name` varchar(255) DEFAULT NULL COMMENT 'Nom original du fichier uploadé', + `fk_entite` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'entité propriétaire', + `fk_operation` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'opération (pour passages)', + `file_path` varchar(500) DEFAULT NULL COMMENT 'Chemin complet du fichier', + `original_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur originale de l\'image', + `original_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur originale de l\'image', + `processed_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur après traitement', + `processed_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur après traitement', + `is_processed` tinyint(1) unsigned DEFAULT 0 COMMENT 'Image redimensionnée (1) ou originale (0)', + `description` varchar(100) NOT NULL DEFAULT '' COMMENT 'Description du fichier', + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + `fk_user_creat` int(10) unsigned NOT NULL DEFAULT 0, + `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(), + `fk_user_modif` int(10) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `id_UNIQUE` (`id`), + KEY `idx_entite` (`fk_entite`), + KEY `idx_operation` (`fk_operation`), + KEY `idx_support_type` (`support`, `support_id`), + KEY `idx_file_type` (`file_type`), + CONSTRAINT `fk_medias_entite` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT `fk_medias_operation` FOREIGN KEY (`fk_operation`) REFERENCES `operations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +``` + +### Migration SQL pour table existante + +```sql +-- Ajout des nouvelles colonnes à la table existante +ALTER TABLE `medias` +ADD COLUMN `file_type` varchar(50) DEFAULT NULL COMMENT 'Extension du fichier (pdf, jpg, xlsx, etc.)' AFTER `fichier`, +ADD COLUMN `file_size` int(10) unsigned DEFAULT NULL COMMENT 'Taille du fichier en octets' AFTER `file_type`, +ADD COLUMN `mime_type` varchar(100) DEFAULT NULL COMMENT 'Type MIME du fichier' AFTER `file_size`, +ADD COLUMN `original_name` varchar(255) DEFAULT NULL COMMENT 'Nom original du fichier uploadé' AFTER `mime_type`, +ADD COLUMN `fk_entite` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'entité propriétaire' AFTER `support_id`, +ADD COLUMN `fk_operation` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'opération (pour passages)' AFTER `fk_entite`, +ADD COLUMN `file_path` varchar(500) DEFAULT NULL COMMENT 'Chemin complet du fichier' AFTER `original_name`, +ADD COLUMN `original_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur originale de l\'image' AFTER `file_path`, +ADD COLUMN `original_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur originale de l\'image' AFTER `original_width`, +ADD COLUMN `processed_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur après traitement' AFTER `original_height`, +ADD COLUMN `processed_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur après traitement' AFTER `processed_width`, +ADD COLUMN `is_processed` tinyint(1) unsigned DEFAULT 0 COMMENT 'Image redimensionnée (1) ou originale (0)' AFTER `processed_height`; + +-- Ajout des index pour optimiser les requêtes +ALTER TABLE `medias` +ADD INDEX `idx_entite` (`fk_entite`), +ADD INDEX `idx_operation` (`fk_operation`), +ADD INDEX `idx_support_type` (`support`, `support_id`), +ADD INDEX `idx_file_type` (`file_type`); + +-- Ajout des contraintes de clés étrangères +ALTER TABLE `medias` +ADD CONSTRAINT `fk_medias_entite` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ON UPDATE CASCADE ON DELETE CASCADE, +ADD CONSTRAINT `fk_medias_operation` FOREIGN KEY (`fk_operation`) REFERENCES `operations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE; +``` + +## Types de Support + +### 1. Entité (`support = 'entite'`) + +- **Fichiers autorisés** : PDF, Excel, Images (JPG, PNG) +- **Taille max** : 20 MB +- **Usage** : Documents généraux de l'entité (règlements, statuts, etc.) +- **Chemin** : `uploads/entites/{entite_id}/documents/` + +### 2. Utilisateur (`support = 'user'`) + +- **Fichiers autorisés** : Images uniquement (JPG, PNG, GIF, WebP) +- **Taille max** : 5 MB +- **Usage** : Avatars, photos de profil +- **Chemin** : `uploads/entites/{entite_id}/users/{user_id}/` +- **Traitement** : Redimensionnement automatique + +### 3. Opération (`support = 'operation'`) + +- **Fichiers autorisés** : Excel uniquement (XLS, XLSX) +- **Taille max** : 20 MB +- **Usage** : Plannings, listes, données d'opération +- **Chemin** : `uploads/entites/{entite_id}/operations/{operation_id}/documents/` + +### 4. Passage (`support = 'passage'`) + +- **Fichiers autorisés** : PDF et Images (JPG, PNG, PDF) +- **Taille max** : 10 MB par fichier +- **Usage** : Reçus, photos de passage, justificatifs +- **Chemin** : `uploads/entites/{entite_id}/operations/{operation_id}/passages/{passage_id}/` +- **Traitement** : Redimensionnement automatique pour les images + +## Traitement Automatique des Images + +### Règles de redimensionnement + +- **Dimension maximale** : 250px (hauteur ou largeur, selon la plus grande) +- **Résolution** : 72 DPI (optimisé web) +- **Préservation du ratio** : Redimensionnement proportionnel +- **Formats supportés** : JPG, PNG, GIF, WebP +- **Qualité JPEG** : 85% (bon compromis qualité/poids) + +### Exemples de transformation + +``` +Image originale 1000x800px → Image traitée 250x200px +Image originale 600x1200px → Image traitée 125x250px +Image originale 200x150px → Pas de redimensionnement (déjà < 250px) +``` + +### Workflow de traitement + +1. **Upload** → Validation du type MIME +2. **Analyse** → Détection des dimensions originales +3. **Traitement** → Redimensionnement si nécessaire +4. **Optimisation** → Compression et résolution web +5. **Sauvegarde** → Image optimisée + métadonnées +6. **Nettoyage** → Suppression du fichier temporaire + +## API Endpoints + +### Routes de gestion des fichiers + +```php +// Upload de fichiers +POST /api/medias/upload +Content-Type: multipart/form-data +Body: { + "file": [fichier], + "support": "entite|user|operation|passage", + "support_id": 123, + "description": "Description du fichier" +} + +// Récupération d'un fichier +GET /api/medias/{id} + +// Liste des fichiers par support +GET /api/medias/list/{support}/{support_id} + +// Suppression d'un fichier +DELETE /api/medias/{id} +``` + +### Exemples de requêtes + +#### Upload d'un avatar utilisateur + +```bash +curl -X POST "https://api.geosector.fr/medias/upload" \ + -H "Authorization: Bearer {token}" \ + -F "file=@avatar.jpg" \ + -F "support=user" \ + -F "support_id=123" \ + -F "description=Avatar utilisateur" +``` + +#### Upload d'une photo de passage + +```bash +curl -X POST "https://api.geosector.fr/medias/upload" \ + -H "Authorization: Bearer {token}" \ + -F "file=@photo_passage.jpg" \ + -F "support=passage" \ + -F "support_id=789" \ + -F "description=Photo du passage" +``` + +## Sécurité et Contrôles + +### Validation des fichiers + +- **Types MIME** : Vérification stricte du type de fichier +- **Extensions** : Validation de l'extension par rapport au contenu +- **Taille** : Limite selon le type de support +- **Contenu** : Scan antivirus recommandé en production + +### Contrôles d'accès + +- **Authentification** : Token JWT requis +- **Autorisation** : Utilisateur ne peut accéder qu'aux fichiers de son entité +- **Vérification** : Contrôle que l'utilisateur appartient à l'entité du fichier +- **Logs** : Traçabilité complète des uploads et accès + +### Nommage des fichiers + +```php +// Format : {timestamp}_{random}_{sanitized_name}.{extension} +// Exemple : 1640995200_a1b2c3_document_reglement.pdf +``` + +## Gestion des Erreurs + +### Codes d'erreur HTTP + +- **400** : Fichier invalide ou paramètres manquants +- **401** : Non authentifié +- **403** : Accès refusé à cette entité +- **413** : Fichier trop volumineux +- **415** : Type de fichier non supporté +- **500** : Erreur serveur lors du traitement + +### Messages d'erreur + +```json +{ + "status": "error", + "message": "Type de fichier non autorisé pour ce support", + "code": "INVALID_FILE_TYPE", + "allowed_types": ["jpg", "png", "gif", "webp"] +} +``` + +## Maintenance et Nettoyage + +### Nettoyage automatique + +- **Fichiers temporaires** : Suppression après 24h +- **Fichiers orphelins** : Détection et suppression des fichiers sans référence en base +- **Anciennes opérations** : Suppression en cascade lors de la suppression d'une opération + +### Commandes de maintenance + +```bash +# Nettoyage des fichiers temporaires +php scripts/cleanup_temp_files.php + +# Détection des fichiers orphelins +php scripts/find_orphan_files.php + +# Statistiques d'utilisation +php scripts/storage_stats.php +``` + +## Performances et Optimisation + +### Optimisations + +- **CDN** : Recommandé pour la distribution des fichiers +- **Cache** : Headers de cache appropriés pour les fichiers statiques +- **Compression** : Gzip pour les réponses API +- **Index** : Index optimisés sur la table medias + +### Monitoring + +- **Espace disque** : Surveillance de l'utilisation +- **Performance** : Temps de traitement des images +- **Erreurs** : Logs des échecs d'upload et de traitement + +## Exemples d'Utilisation + +### Cas d'usage typiques + +1. **Upload d'avatar utilisateur** + + - Fichier JPG de 2MB + - Redimensionnement automatique à 250x250px + - Stockage dans `uploads/entites/5/users/123/` + +2. **Document d'opération** + + - Fichier Excel de planning + - Stockage dans `uploads/entites/5/operations/2644/documents/` + - Pas de traitement (fichier conservé tel quel) + +3. **Photo de passage** + - Photo JPG de 8MB prise sur mobile + - Redimensionnement automatique à 250px max + - Stockage dans `uploads/entites/5/operations/2644/passages/789/` + +### Intégration frontend + +```javascript +// Upload avec progress +const uploadFile = async (file, support, supportId, description) => { + const formData = new FormData(); + formData.append('file', file); + formData.append('support', support); + formData.append('support_id', supportId); + formData.append('description', description); + + const response = await fetch('/api/medias/upload', { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }); + + return response.json(); +}; +``` + +--- + +**Version** : 1.0 +**Date** : Juin 2025 +**Auteur** : API Geosector Team diff --git a/api/docs/geo_app.sql b/api/docs/geo_app.sql index 810a0113..f82412a8 100644 --- a/api/docs/geo_app.sql +++ b/api/docs/geo_app.sql @@ -237,17 +237,37 @@ CREATE TABLE `entites` ( CREATE TABLE `medias` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `support` varchar(45) NOT NULL DEFAULT '', - `support_id` int(10) unsigned NOT NULL DEFAULT 0, - `fichier` varchar(250) NOT NULL DEFAULT '', - `description` varchar(100) NOT NULL DEFAULT '', + `support` varchar(45) NOT NULL DEFAULT '' COMMENT 'Type de support (entite, user, operation, passage)', + `support_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'ID de élément associé', + `fichier` varchar(250) NOT NULL DEFAULT '' COMMENT 'Nom du fichier stocké', + `file_type` varchar(50) DEFAULT NULL COMMENT 'Extension du fichier (pdf, jpg, xlsx, etc.)', + `file_category` varchar(50) DEFAULT NULL COMMENT 'export, logo, carte, etc.', + `file_size` int(10) unsigned DEFAULT NULL COMMENT 'Taille du fichier en octets', + `mime_type` varchar(100) DEFAULT NULL COMMENT 'Type MIME du fichier', + `original_name` varchar(255) DEFAULT NULL COMMENT 'Nom original du fichier uploadé', + `fk_entite` int(10) unsigned DEFAULT NULL COMMENT 'ID de entité propriétaire', + `fk_operation` int(10) unsigned DEFAULT NULL COMMENT 'ID de opération (pour passages)', + `file_path` varchar(500) DEFAULT NULL COMMENT 'Chemin complet du fichier', + `original_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur originale de image', + `original_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur originale de image', + `processed_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur après traitement', + `processed_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur après traitement', + `is_processed` tinyint(1) unsigned DEFAULT 0 COMMENT 'Image redimensionnée (1) ou originale (0)', + `description` varchar(100) NOT NULL DEFAULT '' COMMENT 'Description du fichier', `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `fk_user_creat` int(10) unsigned NOT NULL DEFAULT 0, `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(), `fk_user_modif` int(10) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), - UNIQUE KEY `id_UNIQUE` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=176 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `PAGE_COMPRESSED`='ON'; + UNIQUE KEY `id_UNIQUE` (`id`), + KEY `idx_entite` (`fk_entite`), + KEY `idx_operation` (`fk_operation`), + KEY `idx_support_type` (`support`, `support_id`), + KEY `idx_file_type` (`file_type`), + KEY `idx_file_category` (`file_category`), + CONSTRAINT `fk_medias_entite` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT `fk_medias_operation` FOREIGN KEY (`fk_operation`) REFERENCES `operations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE `ope_pass` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, @@ -375,22 +395,6 @@ CREATE TABLE `ope_users_sectors` ( CONSTRAINT `ope_users_sectors_ibfk_3` FOREIGN KEY (`fk_sector`) REFERENCES `ope_sectors` (`id`) ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=48082 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `PAGE_COMPRESSED`='ON'; -CREATE TABLE `ope_users_suivis` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `fk_operation` int(10) unsigned NOT NULL DEFAULT 0, - `fk_user` int(10) unsigned NOT NULL DEFAULT 0, - `date_suivi` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Date du suivi', - `gps_lat` varchar(20) NOT NULL DEFAULT '', - `gps_lng` varchar(20) NOT NULL DEFAULT '', - `vitesse` varchar(20) NOT NULL DEFAULT '', - `created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Date de création', - `fk_user_creat` int(10) unsigned NOT NULL DEFAULT 0, - `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp() COMMENT 'Date de modification', - `fk_user_modif` int(10) unsigned NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `id_UNIQUE` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `PAGE_COMPRESSED`='ON'; - CREATE TABLE `operations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `fk_entite` int(10) unsigned NOT NULL DEFAULT 1, diff --git a/api/export_operation.php b/api/export_operation.php new file mode 100644 index 00000000..6c0f1354 --- /dev/null +++ b/api/export_operation.php @@ -0,0 +1,145 @@ +prepare($sql); + // $stmt->execute(); + // return $stmt->fetchAll(PDO::FETCH_ASSOC); + return []; +} + +function eLog($message) { + error_log($message); +} + +switch ($Route->_action) { + case "export_operation": + $data = json_decode(file_get_contents("php://input")); + if (isset($data->cid)) { + $cid = nettoie_input($data->cid); + $idMembre = "0"; + $libMembre = ""; + if (isset($data->idMembre) && isset($data->libMembre)) { + $idMembre = nettoie_input($data->idMembre); + $libMembre = nettoie_input($data->libMembre); + } + + // On crée le dossier de l'amicale s'il n'est pas déjà créé + $dir = 'pub/files/upload/' . $Conf->_entite["rowid"]; + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $sql = 'SELECT p.date_eve, u.prenom, u.libelle AS nom, u.nom_tournee, p.fk_type, p.numero, p.rue_bis, p.rue, p.ville, p.fk_habitat, p.appt, p.niveau, p.libelle, p.email, p.phone, p.montant, xtr.libelle AS reglement, p.remarque FROM ope_pass p LEFT JOIN users u ON u.rowid=p.fk_user LEFT JOIN x_types_reglements xtr ON xtr.rowid=p.fk_type_reglement WHERE p.fk_operation=' . $cid; + if ($idMembre != "0") { + $sql .= ' AND p.fk_user=' . $idMembre . ';'; + } + $pass = getinfos($sql); + + $aData = array(); + $aData[] = array( + 'Date', + 'Heure', + 'Prenom', + 'Nom', + 'Tournee', + 'Type', + 'N°', + 'Rue', + 'Ville', + 'Habitat', + 'Donateur', + 'Email', + 'Tel', + 'Montant', + 'Reglement', + 'Remarque' + ); + foreach ($pass as $p) { + switch ($p["fk_type"]) { + case 1: + $ptype = "Effectué"; + $preglement = $p["reglement"]; + break; + case 2: + $ptype = "A finaliser"; + $preglement = ""; + break; + case 3: + $ptype = "Refusé"; + $preglement = ""; + break; + case 4: + $ptype = "Don"; + $preglement = ""; + break; + case 9: + $ptype = "Habitat vide"; + $preglement = ""; + break; + default: + $ptype = $p["fk_type"]; + $preglement = ""; + break; + } + if ($p["fk_habitat"] == 1) { + $phabitat = "Individuel"; + } else { + $phabitat = "Etage " . $p["niveau"] . " - Appt " . $p["appt"]; + } + $dateEve = date("d/m/Y", strtotime($p["date_eve"])); + $heureEve = date("H:i", strtotime($p["date_eve"])); + $nom = str_replace("/", "-", $p["nom"]); + $tournee = str_replace("/", "-", $p["nom_tournee"]); + $aData[] = array( + $dateEve, + $heureEve, + $p["prenom"], + $nom, + $tournee, + $ptype, + $p["numero"] . $p["rue_bis"], + $p["rue"], + $p["ville"], + $phabitat, + $p["libelle"], + $p["email"], + $p["phone"], + $p["montant"], + $preglement, + $p["remarque"] + ); + } + $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + $activeWorksheet = $spreadsheet->getActiveSheet() + ->fromArray($aData, null, 'A1'); + + $writer = new Xlsx($spreadsheet); + if ($idMembre == "0") { + $xlsxName = $dir . '/geosector-ope-' . $cid . '-' . date("Ymd-His") . '.xlsx'; + } else { + $libMembre = str_replace("/", "-", $libMembre); + $libMembre = str_replace("*", "-", $libMembre); + $xlsxName = $dir . '/geosector-ope-' . $cid . '-' . $libMembre . '-' . date("Ymd-His") . '.xlsx'; + } + eLog("Export Operation : " . $xlsxName); + $writer->save($xlsxName); + + $ret = array('url' => $xlsxName); + echo json_encode($ret); + } + break; +} diff --git a/api/index.php b/api/index.php index e0a2828e..4add79d2 100644 --- a/api/index.php +++ b/api/index.php @@ -19,6 +19,7 @@ require_once __DIR__ . '/src/Controllers/LogController.php'; require_once __DIR__ . '/src/Controllers/LoginController.php'; require_once __DIR__ . '/src/Controllers/EntiteController.php'; require_once __DIR__ . '/src/Controllers/UserController.php'; +require_once __DIR__ . '/src/Controllers/OperationController.php'; // Initialiser la configuration $appConfig = AppConfig::getInstance(); diff --git a/api/livre-api.sh b/api/livre-api.sh index 7a2775ae..04a1c4f0 100755 --- a/api/livre-api.sh +++ b/api/livre-api.sh @@ -1,9 +1,14 @@ #!/bin/bash # Vérification des arguments -if [ $# -ne 2 ]; then - echo "Usage: $0 " - echo "Example: $0 dva-geo rca-geo" +if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo " rec : Livrer de DVA (dva-geo) vers RECETTE (rca-geo)" + echo " prod : Livrer de RECETTE (rca-geo) vers PRODUCTION (pra-geo)" + echo "" + echo "Examples:" + echo " $0 rec # DVA → RECETTE" + echo " $0 prod # RECETTE → PRODUCTION" exit 1 fi @@ -12,14 +17,31 @@ HOST_USER=root HOST_KEY=/Users/pierre/.ssh/id_rsa_mbpi HOST_PORT=22 -SOURCE_CONTAINER=$1 -DEST_CONTAINER=$2 +# Mapping des environnements +ENVIRONMENT=$1 +case $ENVIRONMENT in + "rec") + SOURCE_CONTAINER="dva-geo" + DEST_CONTAINER="rca-geo" + ENV_NAME="RECETTE" + ;; + "prod") + SOURCE_CONTAINER="rca-geo" + DEST_CONTAINER="pra-geo" + ENV_NAME="PRODUCTION" + ;; + *) + echo "❌ Environnement '$ENVIRONMENT' non reconnu" + echo "Utilisez 'rec' pour RECETTE ou 'prod' pour PRODUCTION" + exit 1 + ;; +esac API_PATH="/var/www/geosector/api" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") BACKUP_DIR="${API_PATH}_backup_${TIMESTAMP}" PROJECT="default" -echo "🔄 Copie de l'API de $SOURCE_CONTAINER vers $DEST_CONTAINER (projet: $PROJECT)" +echo "🔄 Livraison vers $ENV_NAME : $SOURCE_CONTAINER → $DEST_CONTAINER (projet: $PROJECT)" # Vérifier si les containers existent echo "🔍 Vérification des containers..." @@ -47,37 +69,24 @@ else echo "⚠️ Le dossier API n'existe pas sur la destination" fi -# Sauvegarder spécifiquement le dossier logs -echo "📋 Sauvegarde du dossier logs..." -# Vérifier si le dossier logs existe -ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- test -d $API_PATH/logs" -if [ $? -eq 0 ]; then - # Le dossier logs existe, le sauvegarder - ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- mkdir -p /tmp/geosector_logs_backup" - ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- cp -r $API_PATH/logs /tmp/geosector_logs_backup/" - echo "✅ Dossier logs sauvegardé temporairement" -else - echo "⚠️ Le dossier logs n'existe pas sur la destination" -fi - # Copier le dossier API entre les containers echo "📋 Copie des fichiers en cours..." -# Approche directe: utiliser incus copy pour copier directement entre containers -echo "📤 Transfert direct entre containers..." -# Nettoyer le dossier de destination -ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- rm -rf $API_PATH" -ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- mkdir -p $API_PATH" +# Nettoyage sélectif : supprimer seulement le code, pas les données (logs et uploads) +echo "🧹 Nettoyage sélectif (préservation de logs et uploads)..." +ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- find $API_PATH -mindepth 1 -maxdepth 1 ! -name 'uploads' ! -name 'logs' -exec rm -rf {} \;" -# Copier directement du container source vers le container destination -ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $SOURCE_CONTAINER --project $PROJECT -- tar -cf - -C $API_PATH . | incus exec $DEST_CONTAINER --project $PROJECT -- tar -xf - -C $API_PATH" +# Copier directement du container source vers le container destination (en excluant logs et uploads) +echo "📤 Transfert du code (hors logs et uploads)..." +ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $SOURCE_CONTAINER --project $PROJECT -- tar -cf - -C $API_PATH --exclude='uploads' --exclude='logs' . | incus exec $DEST_CONTAINER --project $PROJECT -- tar -xf - -C $API_PATH" if [ $? -ne 0 ]; then - echo "❌ Erreur lors du transfert direct entre containers" + echo "❌ Erreur lors du transfert entre containers" echo "⚠️ Tentative de restauration de la sauvegarde..." # Vérifier si la sauvegarde existe ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- test -d $BACKUP_DIR" if [ $? -eq 0 ]; then # La sauvegarde existe, la restaurer + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- rm -rf $API_PATH" ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- cp -r $BACKUP_DIR $API_PATH" echo "✅ Restauration réussie" else @@ -86,21 +95,7 @@ if [ $? -ne 0 ]; then exit 1 fi -# Pas de fichiers temporaires à nettoyer avec cette approche - -# Restaurer le dossier logs -echo "📋 Restauration du dossier logs..." -# Vérifier si la sauvegarde des logs existe -ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- test -d /tmp/geosector_logs_backup/logs" -if [ $? -eq 0 ]; then - # La sauvegarde des logs existe, la restaurer - ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- mkdir -p $API_PATH/logs" - ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- cp -r /tmp/geosector_logs_backup/logs/* $API_PATH/logs/" - ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- rm -rf /tmp/geosector_logs_backup" - echo "✅ Dossier logs restauré" -else - echo "⚠️ Aucune sauvegarde de logs à restaurer" -fi +echo "✅ Code transféré avec succès (logs et uploads préservés)" # Changer le propriétaire et les permissions des fichiers echo "👤 Application des droits et permissions pour tous les fichiers..." @@ -128,6 +123,23 @@ else echo "⚠️ Le dossier logs n'existe pas" fi +# Vérifier et corriger les permissions du dossier uploads s'il existe +ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- test -d $API_PATH/uploads" +if [ $? -eq 0 ]; then + # S'assurer que uploads a les bonnes permissions + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- chown -R nginx:nobody $API_PATH/uploads" + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- chmod -R 775 $API_PATH/uploads" + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- find $API_PATH/uploads -type f -exec chmod 664 {} \;" + echo "✅ Droits vérifiés pour le dossier uploads (nginx:nginx avec permissions 775)" +else + # Créer le dossier uploads s'il n'existe pas + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- mkdir -p $API_PATH/uploads" + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- chown -R nginx:nobody $API_PATH/uploads" + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- chmod -R 775 $API_PATH/uploads" + ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- find $API_PATH/uploads -type f -exec chmod 664 {} \;" + echo "✅ Dossier uploads créé avec les bonnes permissions (nginx:nginx avec permissions 775/664)" +fi + echo "✅ Propriétaire et permissions appliqués avec succès" # Vérifier la copie @@ -139,6 +151,8 @@ else echo "❌ Erreur: Le dossier API n'a pas été copié correctement" fi -echo "✅ Opération terminée! L'API a été copiée de $SOURCE_CONTAINER vers $DEST_CONTAINER" -echo "📁 Une sauvegarde a été créée dans $BACKUP_DIR sur $DEST_CONTAINER" -echo "👤 Les fichiers appartiennent maintenant à l'utilisateur nginx" +echo "✅ Livraison vers $ENV_NAME terminée avec succès!" +echo "📤 Source: $SOURCE_CONTAINER → Destination: $DEST_CONTAINER" +echo "📁 Sauvegarde créée: $BACKUP_DIR sur $DEST_CONTAINER" +echo "🔒 Données préservées: logs/ et uploads/ intouchés" +echo "👤 Permissions: nginx:nginx (755/644) + logs (nginx:nobody 775/664)" diff --git a/api/migration_add_file_category.sql b/api/migration_add_file_category.sql new file mode 100644 index 00000000..556c0c04 --- /dev/null +++ b/api/migration_add_file_category.sql @@ -0,0 +1,26 @@ +-- Migration pour ajouter la colonne file_category à la table medias +-- Date: 2025-06-22 +-- Description: Ajout du champ file_category pour distinguer les types métier des fichiers + +-- Ajout de la colonne file_category +ALTER TABLE `medias` +ADD COLUMN `file_category` varchar(50) DEFAULT NULL COMMENT 'Catégorie du fichier (logo, carte, photo, document, etc.)' AFTER `file_type`; + +-- Ajout de l'index pour optimiser les requêtes +ALTER TABLE `medias` +ADD INDEX `idx_file_category` (`file_category`); + +-- Mise à jour des données existantes avec des catégories par défaut selon le support +UPDATE `medias` SET `file_category` = 'document' WHERE `support` = 'entite' AND `file_category` IS NULL; +UPDATE `medias` SET `file_category` = 'avatar' WHERE `support` = 'user' AND `file_category` IS NULL; +UPDATE `medias` SET `file_category` = 'export' WHERE `support` = 'operation' AND `file_category` IS NULL; +UPDATE `medias` SET `file_category` = 'recu' WHERE `support` = 'passage' AND `file_category` IS NULL; + +-- Vérification des modifications +SELECT + support, + file_category, + COUNT(*) as count +FROM medias +GROUP BY support, file_category +ORDER BY support, file_category; diff --git a/api/migration_add_ope_users_fields.sql b/api/migration_add_ope_users_fields.sql new file mode 100644 index 00000000..dc3cb0b7 --- /dev/null +++ b/api/migration_add_ope_users_fields.sql @@ -0,0 +1,16 @@ +-- Migration pour ajouter les champs utilisateur dans ope_users +-- Date: 2025-06-23 +-- Description: Ajout des champs fk_role, first_name, encrypted_name, sect_name dans ope_users +-- pour conserver un historique propre de chaque opération + +USE geo_app; + +-- Ajout des nouvelles colonnes dans ope_users +ALTER TABLE ope_users +ADD COLUMN fk_role int unsigned DEFAULT 1 AFTER fk_user, +ADD COLUMN first_name varchar(45) DEFAULT '' AFTER fk_role, +ADD COLUMN encrypted_name varchar(255) DEFAULT '' AFTER first_name, +ADD COLUMN sect_name varchar(60) DEFAULT '' AFTER encrypted_name; + +-- Vérification de la structure modifiée +DESCRIBE ope_users; diff --git a/api/src/Config/AppConfig.php b/api/src/Config/AppConfig.php index 81f22042..e22a9ced 100644 --- a/api/src/Config/AppConfig.php +++ b/api/src/Config/AppConfig.php @@ -71,6 +71,12 @@ class AppConfig { 'api_key' => '', // À remplir avec la clé API SMS OVH 'api_secret' => '', // À remplir avec le secret API SMS OVH ], + 'backup' => [ + 'encryption_key' => 'K8mN2pQ5rT9wX3zA6bE1fH4jL7oS0vY2', // Clé de 32 caractères pour AES-256 + 'compression' => true, + 'compression_level' => 6, + 'cipher' => 'AES-256-CBC' + ], ]; // Configuration PRODUCTION @@ -336,6 +342,24 @@ class AppConfig { return $this->clientIp; } + /** + * Retourne la configuration des backups + * + * @return array Configuration des backups + */ + public function getBackupConfig(): array { + return $this->getCurrentConfig()['backup']; + } + + /** + * Retourne la clé de chiffrement des backups + * + * @return string Clé de chiffrement des backups + */ + public function getBackupEncryptionKey(): string { + return $this->getCurrentConfig()['backup']['encryption_key']; + } + /** * Détermine l'adresse IP du client en tenant compte des proxys et load balancers * diff --git a/api/src/Controllers/FileController.php b/api/src/Controllers/FileController.php new file mode 100644 index 00000000..ade72e98 --- /dev/null +++ b/api/src/Controllers/FileController.php @@ -0,0 +1,1066 @@ + ['logo', 'document', 'reglement', 'statut'], + 'user' => ['avatar', 'photo'], + 'operation' => ['planning', 'liste', 'export', 'backup'], + 'passage' => ['recu', 'photo', 'justificatif', 'carte'] + ]; + + // Extensions autorisées + private const ALLOWED_EXTENSIONS = [ + 'pdf', + 'jpg', + 'jpeg', + 'png', + 'gif', + 'webp', + 'xlsx', + 'xls', + 'json', + 'csv' + ]; + + public function __construct() { + $this->db = Database::getInstance(); + $this->appConfig = AppConfig::getInstance(); + } + + /** + * Récupère les informations utilisateur (rôle et entité) + */ + private function getUserInfo(int $userId): ?array { + try { + $stmt = $this->db->prepare('SELECT fk_entite, fk_role FROM users WHERE id = ?'); + $stmt->execute([$userId]); + return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des infos utilisateur', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId + ]); + return null; + } + } + + /** + * Valide qu'un chemin est autorisé pour l'utilisateur + */ + private function validatePath(string $path, int $userRole, int $userEntiteId): bool { + // Empêcher les traversées de répertoire + if (strpos($path, '..') !== false || strpos($path, './') !== false) { + return false; + } + + // Normaliser le chemin + $path = trim($path, '/'); + + // Super admin : accès total + if ($userRole > 2) { + return true; + } + + // Admin entité : limité à son entité + if ($userRole == 2) { + return strpos($path, "entites/{$userEntiteId}") === 0 || $path === "entites/{$userEntiteId}"; + } + + return false; + } + + /** + * Vérifie si l'utilisateur peut accéder à un fichier + */ + private function canAccessFile(int $fileId, int $userRole, int $userEntiteId): bool { + try { + $stmt = $this->db->prepare('SELECT fk_entite FROM medias WHERE id = ?'); + $stmt->execute([$fileId]); + $file = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$file) { + return false; + } + + // Super admin : accès total + if ($userRole > 2) { + return true; + } + + // Admin entité : seulement ses fichiers + return (int)$file['fk_entite'] === $userEntiteId; + } catch (Exception $e) { + LogService::log('Erreur lors de la vérification d\'accès au fichier', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'fileId' => $fileId, + 'userId' => Session::getUserId() + ]); + return false; + } + } + + /** + * Extrait et valide les paramètres de filtrage + */ + private function extractFilters(array $params): array { + return [ + 'page' => max(1, (int)($params['page'] ?? 1)), + 'per_page' => min(100, max(1, (int)($params['per_page'] ?? 50))), + 'search' => !empty($params['search']) ? trim($params['search']) : null, + 'type' => !empty($params['type']) && in_array($params['type'], self::ALLOWED_EXTENSIONS) ? $params['type'] : null, + 'category' => !empty($params['category']) ? trim($params['category']) : null, + 'sort' => in_array($params['sort'] ?? '', ['name', 'date', 'size', 'type']) ? $params['sort'] : 'date', + 'order' => in_array($params['order'] ?? '', ['asc', 'desc']) ? $params['order'] : 'desc' + ]; + } + + /** + * Construit la clause WHERE pour les requêtes de recherche + */ + private function buildWhereClause(array $filters, int $userRole, int $userEntiteId, ?string $path = null): array { + $conditions = []; + $params = []; + + // Restriction par entité selon le rôle + if ($userRole == 2) { + $conditions[] = 'm.fk_entite = ?'; + $params[] = $userEntiteId; + } + + // Filtrage par chemin si spécifié + if ($path !== null) { + $conditions[] = 'm.file_path LIKE ?'; + $params[] = "uploads/{$path}%"; + } + + // Recherche textuelle + if ($filters['search']) { + $searchTerm = '%' . $filters['search'] . '%'; + $conditions[] = '(m.fichier LIKE ? OR m.original_name LIKE ? OR m.description LIKE ?)'; + $params[] = $searchTerm; + $params[] = $searchTerm; + $params[] = $searchTerm; + } + + // Filtrage par type (extension) + if ($filters['type']) { + $conditions[] = 'm.file_type = ?'; + $params[] = $filters['type']; + } + + // Filtrage par catégorie + if ($filters['category']) { + $conditions[] = 'm.file_category = ?'; + $params[] = $filters['category']; + } + + $whereClause = !empty($conditions) ? 'WHERE ' . implode(' AND ', $conditions) : ''; + + return [$whereClause, $params]; + } + + /** + * Construit la clause ORDER BY + */ + private function buildOrderClause(array $filters): string { + $sortField = match ($filters['sort']) { + 'name' => 'm.original_name', + 'size' => 'm.file_size', + 'type' => 'm.file_type', + default => 'm.created_at' + }; + + return "ORDER BY {$sortField} {$filters['order']}"; + } + + /** + * Navigation dans l'arborescence avec recherche et pagination + */ + public function browse(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + + $path = $_GET['path'] ?? ''; + $filters = $this->extractFilters($_GET); + + // Validation du chemin + if (!$this->validatePath($path, $userRole, $userEntiteId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Accès refusé à ce répertoire' + ], 403); + return; + } + + // Construction de la requête + [$whereClause, $params] = $this->buildWhereClause($filters, $userRole, $userEntiteId, $path); + $orderClause = $this->buildOrderClause($filters); + + // Requête pour compter le total + $countSql = " + SELECT COUNT(*) as total + FROM medias m + {$whereClause} + "; + + $stmt = $this->db->prepare($countSql); + $stmt->execute($params); + $totalItems = (int)$stmt->fetchColumn(); + + // Calcul de la pagination + $totalPages = ceil($totalItems / $filters['per_page']); + $offset = ($filters['page'] - 1) * $filters['per_page']; + + // Requête principale avec pagination + $sql = " + SELECT + m.id, m.fichier, m.original_name, m.file_type, m.file_category, + m.file_size, m.file_path, m.description, m.created_at, + m.fk_user_creat, u.encrypted_name as creator_name + FROM medias m + LEFT JOIN users u ON u.id = m.fk_user_creat + {$whereClause} + {$orderClause} + LIMIT ? OFFSET ? + "; + + $params[] = $filters['per_page']; + $params[] = $offset; + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $files = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrer les noms des créateurs + foreach ($files as &$file) { + if ($file['creator_name']) { + $file['creator_name'] = ApiService::decryptData($file['creator_name']); + } + unset($file['encrypted_name']); + } + + // Statistiques rapides + $statsSql = " + SELECT + COUNT(*) as total_files, + SUM(m.file_size) as total_size, + m.file_category, + COUNT(*) as category_count + FROM medias m + {$whereClause} + GROUP BY m.file_category + "; + + $stmt = $this->db->prepare($statsSql); + $stmt->execute(array_slice($params, 0, -2)); // Enlever LIMIT et OFFSET + $stats = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $summary = [ + 'total_files' => $totalItems, + 'total_size' => array_sum(array_column($stats, 'total_size')), + 'by_category' => [] + ]; + + foreach ($stats as $stat) { + if ($stat['file_category']) { + $summary['by_category'][$stat['file_category']] = (int)$stat['category_count']; + } + } + + Response::json([ + 'status' => 'success', + 'current_path' => $path, + 'parent_path' => dirname($path) !== '.' ? dirname($path) : null, + 'pagination' => [ + 'current_page' => $filters['page'], + 'per_page' => $filters['per_page'], + 'total_items' => $totalItems, + 'total_pages' => $totalPages, + 'has_next' => $filters['page'] < $totalPages, + 'has_prev' => $filters['page'] > 1 + ], + 'filters' => [ + 'search' => $filters['search'], + 'type' => $filters['type'], + 'category' => $filters['category'], + 'sort' => $filters['sort'], + 'order' => $filters['order'] + ], + 'files' => $files, + 'summary' => $summary + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la navigation des fichiers', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null, + 'path' => $_GET['path'] ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la navigation des fichiers' + ], 500); + } + } + + /** + * Liste des fichiers par support avec recherche et pagination + */ + public function listBySupport(string $support, string $supportId): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + $supportIdInt = (int)$supportId; + + $filters = $this->extractFilters($_GET); + + // Construction de la requête de base + $conditions = ['m.support = ?', 'm.support_id = ?']; + $params = [$support, $supportIdInt]; + + // Restriction par entité selon le rôle + if ($userRole == 2) { + $conditions[] = 'm.fk_entite = ?'; + $params[] = $userEntiteId; + } + + // Recherche textuelle + if ($filters['search']) { + $searchTerm = '%' . $filters['search'] . '%'; + $conditions[] = '(m.fichier LIKE ? OR m.original_name LIKE ? OR m.description LIKE ?)'; + $params[] = $searchTerm; + $params[] = $searchTerm; + $params[] = $searchTerm; + } + + // Filtrage par type + if ($filters['type']) { + $conditions[] = 'm.file_type = ?'; + $params[] = $filters['type']; + } + + // Filtrage par catégorie + if ($filters['category']) { + $conditions[] = 'm.file_category = ?'; + $params[] = $filters['category']; + } + + $whereClause = 'WHERE ' . implode(' AND ', $conditions); + $orderClause = $this->buildOrderClause($filters); + + // Compter le total + $countSql = "SELECT COUNT(*) as total FROM medias m {$whereClause}"; + $stmt = $this->db->prepare($countSql); + $stmt->execute($params); + $totalItems = (int)$stmt->fetchColumn(); + + // Calcul pagination + $totalPages = ceil($totalItems / $filters['per_page']); + $offset = ($filters['page'] - 1) * $filters['per_page']; + + // Requête principale + $sql = " + SELECT + m.id, m.fichier, m.original_name, m.file_type, m.file_category, + m.file_size, m.file_path, m.description, m.created_at, + m.fk_user_creat, u.encrypted_name as creator_name + FROM medias m + LEFT JOIN users u ON u.id = m.fk_user_creat + {$whereClause} + {$orderClause} + LIMIT ? OFFSET ? + "; + + $params[] = $filters['per_page']; + $params[] = $offset; + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $files = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrer les noms + foreach ($files as &$file) { + if ($file['creator_name']) { + $file['creator_name'] = ApiService::decryptData($file['creator_name']); + } + unset($file['encrypted_name']); + } + + Response::json([ + 'status' => 'success', + 'support' => $support, + 'support_id' => $supportIdInt, + 'pagination' => [ + 'current_page' => $filters['page'], + 'per_page' => $filters['per_page'], + 'total_items' => $totalItems, + 'total_pages' => $totalPages, + 'has_next' => $filters['page'] < $totalPages, + 'has_prev' => $filters['page'] > 1 + ], + 'filters' => [ + 'search' => $filters['search'], + 'type' => $filters['type'], + 'category' => $filters['category'] + ], + 'files' => $files + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la liste des fichiers par support', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'support' => $support, + 'supportId' => $supportId, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des fichiers' + ], 500); + } + } + + /** + * Recherche globale de fichiers + */ + public function search(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + + $query = $_GET['q'] ?? ''; + if (empty(trim($query))) { + Response::json([ + 'status' => 'error', + 'message' => 'Terme de recherche requis' + ], 400); + return; + } + + $filters = $this->extractFilters($_GET); + $filters['search'] = trim($query); + + [$whereClause, $params] = $this->buildWhereClause($filters, $userRole, $userEntiteId); + $orderClause = $this->buildOrderClause($filters); + + // Compter le total + $countSql = "SELECT COUNT(*) as total FROM medias m {$whereClause}"; + $stmt = $this->db->prepare($countSql); + $stmt->execute($params); + $totalItems = (int)$stmt->fetchColumn(); + + // Pagination + $totalPages = ceil($totalItems / $filters['per_page']); + $offset = ($filters['page'] - 1) * $filters['per_page']; + + // Requête principale + $sql = " + SELECT + m.id, m.fichier, m.original_name, m.file_type, m.file_category, + m.file_size, m.file_path, m.description, m.created_at, m.support, + m.support_id, m.fk_user_creat, u.encrypted_name as creator_name + FROM medias m + LEFT JOIN users u ON u.id = m.fk_user_creat + {$whereClause} + {$orderClause} + LIMIT ? OFFSET ? + "; + + $params[] = $filters['per_page']; + $params[] = $offset; + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $files = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrer les noms + foreach ($files as &$file) { + if ($file['creator_name']) { + $file['creator_name'] = ApiService::decryptData($file['creator_name']); + } + unset($file['encrypted_name']); + } + + Response::json([ + 'status' => 'success', + 'query' => $query, + 'pagination' => [ + 'current_page' => $filters['page'], + 'per_page' => $filters['per_page'], + 'total_items' => $totalItems, + 'total_pages' => $totalPages, + 'has_next' => $filters['page'] < $totalPages, + 'has_prev' => $filters['page'] > 1 + ], + 'filters' => [ + 'type' => $filters['type'], + 'category' => $filters['category'] + ], + 'files' => $files + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la recherche de fichiers', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'query' => $_GET['q'] ?? null, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la recherche' + ], 500); + } + } + + /** + * Statistiques d'utilisation des fichiers + */ + public function getStats(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + + if ($userRole == 2) { + // Stats pour admin d'entité + $sql = " + SELECT + COUNT(*) as total_files, + SUM(file_size) as total_size, + support, + file_category, + file_type, + COUNT(*) as count + FROM medias + WHERE fk_entite = ? + GROUP BY support, file_category, file_type + ORDER BY support, file_category + "; + + $stmt = $this->db->prepare($sql); + $stmt->execute([$userEntiteId]); + $stats = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $response = [ + 'status' => 'success', + 'entite_id' => $userEntiteId, + 'storage' => [ + 'total_files' => 0, + 'total_size' => 0, + 'by_support' => [], + 'by_category' => [], + 'by_type' => [] + ] + ]; + + foreach ($stats as $stat) { + $response['storage']['total_files'] += (int)$stat['count']; + $response['storage']['total_size'] += (int)$stat['total_size']; + + $support = $stat['support']; + $category = $stat['file_category'] ?: 'non_categorise'; + $type = $stat['file_type'] ?: 'inconnu'; + + if (!isset($response['storage']['by_support'][$support])) { + $response['storage']['by_support'][$support] = ['count' => 0, 'size' => 0]; + } + $response['storage']['by_support'][$support]['count'] += (int)$stat['count']; + $response['storage']['by_support'][$support]['size'] += (int)$stat['total_size']; + + if (!isset($response['storage']['by_category'][$category])) { + $response['storage']['by_category'][$category] = 0; + } + $response['storage']['by_category'][$category] += (int)$stat['count']; + + if (!isset($response['storage']['by_type'][$type])) { + $response['storage']['by_type'][$type] = 0; + } + $response['storage']['by_type'][$type] += (int)$stat['count']; + } + } else { + // Stats globales pour super admin + $sql = " + SELECT + fk_entite, + COUNT(*) as files, + SUM(file_size) as size + FROM medias + GROUP BY fk_entite + ORDER BY size DESC + "; + + $stmt = $this->db->prepare($sql); + $stmt->execute(); + $entiteStats = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $totalSql = " + SELECT + COUNT(*) as total_files, + SUM(file_size) as total_size, + COUNT(DISTINCT fk_entite) as entites_count + FROM medias + "; + + $stmt = $this->db->prepare($totalSql); + $stmt->execute(); + $totals = $stmt->fetch(PDO::FETCH_ASSOC); + + $response = [ + 'status' => 'success', + 'global_stats' => [ + 'total_files' => (int)$totals['total_files'], + 'total_size' => (int)$totals['total_size'], + 'entites_count' => (int)$totals['entites_count'], + 'by_entite' => [] + ] + ]; + + foreach ($entiteStats as $stat) { + $response['global_stats']['by_entite'][] = [ + 'entite_id' => (int)$stat['fk_entite'], + 'files' => (int)$stat['files'], + 'size' => (int)$stat['size'] + ]; + } + } + + Response::json($response, 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des statistiques', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des statistiques' + ], 500); + } + } + + /** + * Téléchargement sécurisé d'un fichier + */ + public function download(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + $fileId = (int)$id; + + // Vérifier l'accès au fichier + if (!$this->canAccessFile($fileId, $userRole, $userEntiteId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Accès refusé à ce fichier' + ], 403); + return; + } + + // Récupérer les informations du fichier + $stmt = $this->db->prepare(' + SELECT fichier, file_path, mime_type, original_name, file_size + FROM medias + WHERE id = ? + '); + $stmt->execute([$fileId]); + $file = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$file) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier non trouvé' + ], 404); + return; + } + + $filepath = getcwd() . '/' . $file['file_path']; + + if (!file_exists($filepath)) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier physique non trouvé' + ], 404); + return; + } + + // Log du téléchargement + LogService::log('Téléchargement de fichier', [ + 'level' => 'info', + 'userId' => $userId, + 'fileId' => $fileId, + 'filename' => $file['original_name'] + ]); + + // Envoyer le fichier + header('Content-Type: ' . $file['mime_type']); + header('Content-Disposition: attachment; filename="' . $file['original_name'] . '"'); + header('Content-Length: ' . filesize($filepath)); + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: 0'); + + readfile($filepath); + exit; + } catch (Exception $e) { + LogService::log('Erreur lors du téléchargement', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'fileId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors du téléchargement' + ], 500); + } + } + + /** + * Suppression sécurisée d'un fichier + */ + public function deleteFile(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + $fileId = (int)$id; + + // Vérifier l'accès au fichier + if (!$this->canAccessFile($fileId, $userRole, $userEntiteId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Accès refusé à ce fichier' + ], 403); + return; + } + + // Récupérer les informations du fichier + $stmt = $this->db->prepare(' + SELECT fichier, file_path, original_name, support, support_id + FROM medias + WHERE id = ? + '); + $stmt->execute([$fileId]); + $file = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$file) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier non trouvé' + ], 404); + return; + } + + // Supprimer le fichier physique + $filepath = getcwd() . '/' . $file['file_path']; + if (file_exists($filepath)) { + unlink($filepath); + } + + // Supprimer l'enregistrement en base + $stmt = $this->db->prepare('DELETE FROM medias WHERE id = ?'); + $stmt->execute([$fileId]); + + LogService::log('Suppression de fichier', [ + 'level' => 'info', + 'userId' => $userId, + 'fileId' => $fileId, + 'filename' => $file['original_name'], + 'support' => $file['support'], + 'support_id' => $file['support_id'] + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Fichier supprimé avec succès' + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la suppression de fichier', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'fileId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la suppression' + ], 500); + } + } + + /** + * Informations détaillées d'un fichier + */ + public function getFileInfo(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $userInfo = $this->getUserInfo($userId); + if (!$userInfo) { + Response::json([ + 'status' => 'error', + 'message' => 'Informations utilisateur non trouvées' + ], 404); + return; + } + + $userRole = (int)$userInfo['fk_role']; + $userEntiteId = (int)$userInfo['fk_entite']; + $fileId = (int)$id; + + // Vérifier l'accès au fichier + if (!$this->canAccessFile($fileId, $userRole, $userEntiteId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Accès refusé à ce fichier' + ], 403); + return; + } + + // Récupérer toutes les informations du fichier + $stmt = $this->db->prepare(' + SELECT + m.*, + u_creat.encrypted_name as creator_name, + u_modif.encrypted_name as modifier_name + FROM medias m + LEFT JOIN users u_creat ON u_creat.id = m.fk_user_creat + LEFT JOIN users u_modif ON u_modif.id = m.fk_user_modif + WHERE m.id = ? + '); + $stmt->execute([$fileId]); + $file = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$file) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier non trouvé' + ], 404); + return; + } + + // Déchiffrer les noms d'utilisateurs + if ($file['creator_name']) { + $file['creator_name'] = ApiService::decryptData($file['creator_name']); + } + if ($file['modifier_name']) { + $file['modifier_name'] = ApiService::decryptData($file['modifier_name']); + } + + // Vérifier si le fichier physique existe + $filepath = getcwd() . '/' . $file['file_path']; + $file['file_exists'] = file_exists($filepath); + + Response::json([ + 'status' => 'success', + 'file' => $file + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des infos fichier', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'fileId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des informations' + ], 500); + } + } + + /** + * Métadonnées du système de fichiers (catégories, extensions, etc.) + */ + public function getMetadata(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + Response::json([ + 'status' => 'success', + 'categories' => self::FILE_CATEGORIES, + 'extensions' => self::ALLOWED_EXTENSIONS, + 'mime_types' => [ + 'pdf' => 'application/pdf', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xls' => 'application/vnd.ms-excel', + 'json' => 'application/json', + 'csv' => 'text/csv' + ], + 'max_file_sizes' => [ + 'entite' => 20971520, // 20 MB + 'user' => 5242880, // 5 MB + 'operation' => 20971520, // 20 MB + 'passage' => 10485760 // 10 MB + ] + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des métadonnées', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des métadonnées' + ], 500); + } + } +} diff --git a/api/src/Controllers/LoginController.php b/api/src/Controllers/LoginController.php index 1e5c6b5e..ae921bad 100644 --- a/api/src/Controllers/LoginController.php +++ b/api/src/Controllers/LoginController.php @@ -157,7 +157,6 @@ class LoginController { 'first_name' => $user['first_name'] ?? '', 'fk_role' => $user['fk_role'] ?? '0', 'fk_entite' => $user['fk_entite'] ?? '0', - // 'interface' supprimée pour se baser uniquement sur le rôle ]; Session::login($sessionData); @@ -229,13 +228,16 @@ class LoginController { } elseif ($interface === 'admin' && $user['fk_role'] == 2) { // Interface admin avec rôle 2 : les 3 dernières opérations dont l'active $operationLimit = 3; + } elseif ($interface === 'admin' && $user['fk_role'] > 2) { + // Interface admin avec rôle > 2 : les 10 dernières opérations dont l'active + $operationLimit = 10; } else { // Autres cas : pas d'opérations $operationLimit = 0; } if ($operationLimit > 0 && !empty($user['fk_entite'])) { - $operationQuery = "SELECT id, libelle, date_deb, date_fin + $operationQuery = "SELECT id, fk_entite, libelle, date_deb, date_fin, chk_active FROM operations WHERE fk_entite = ?"; @@ -254,9 +256,11 @@ class LoginController { foreach ($operations as $operation) { $operationsData[] = [ 'id' => $operation['id'], - 'name' => $operation['libelle'], + 'fk_entite' => $operation['fk_entite'], + 'libelle' => $operation['libelle'], 'date_deb' => $operation['date_deb'], - 'date_fin' => $operation['date_fin'] + 'date_fin' => $operation['date_fin'], + 'chk_active' => $operation['chk_active'] ]; } @@ -435,21 +439,29 @@ class LoginController { // Déchiffrement du nom if (!empty($membre['encrypted_name'])) { $membreItem['name'] = ApiService::decryptData($membre['encrypted_name']); + } else { + $membreItem['name'] = ''; } // Déchiffrement du nom d'utilisateur if (!empty($membre['encrypted_user_name'])) { $membreItem['username'] = ApiService::decryptSearchableData($membre['encrypted_user_name']); + } else { + $membreItem['username'] = ''; } // Déchiffrement du téléphone if (!empty($membre['encrypted_phone'])) { $membreItem['phone'] = ApiService::decryptData($membre['encrypted_phone']); + } else { + $membreItem['phone'] = ''; } // Déchiffrement du mobile if (!empty($membre['encrypted_mobile'])) { $membreItem['mobile'] = ApiService::decryptData($membre['encrypted_mobile']); + } else { + $membreItem['mobile'] = ''; } // Déchiffrement de l'email @@ -458,6 +470,8 @@ class LoginController { if ($decryptedEmail) { $membreItem['email'] = $decryptedEmail; } + } else { + $membreItem['email'] = ''; } $membresData[] = $membreItem; diff --git a/api/src/Controllers/OperationController.php b/api/src/Controllers/OperationController.php new file mode 100644 index 00000000..5bb9f0ed --- /dev/null +++ b/api/src/Controllers/OperationController.php @@ -0,0 +1,1516 @@ +db = Database::getInstance(); + $this->appConfig = AppConfig::getInstance(); + } + + /** + * Récupère l'entité de l'utilisateur connecté + * + * @param int $userId ID de l'utilisateur + * @return int|null ID de l'entité ou null si non trouvé + */ + private function getUserEntiteId(int $userId): ?int { + try { + $stmt = $this->db->prepare('SELECT fk_entite FROM users WHERE id = ?'); + $stmt->execute([$userId]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + return $user ? (int)$user['fk_entite'] : null; + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération de l\'entité utilisateur', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId + ]); + return null; + } + } + + /** + * Valide les données d'une opération + * + * @param array $data Données à valider + * @param int $entiteId ID de l'entité + * @param int|null $operationId ID de l'opération (pour update) + * @return array|null Erreurs de validation ou null + */ + private function validateOperationData(array $data, int $entiteId, ?int $operationId = null): ?array { + $errors = []; + + // Validation du libellé (accepter 'name' ou 'libelle') + $libelle = $data['libelle'] ?? $data['name'] ?? null; + if (!$libelle || empty(trim($libelle))) { + $errors[] = 'Le nom de l\'opération est obligatoire'; + } else { + $libelle = trim($libelle); + if (strlen($libelle) < 5) { + $errors[] = 'Le nom de l\'opération doit contenir au moins 5 caractères'; + } + if (strlen($libelle) > 75) { + $errors[] = 'Le nom de l\'opération ne peut pas dépasser 75 caractères'; + } + + // Vérifier l'unicité du nom dans l'entité + $sql = 'SELECT COUNT(*) as count FROM operations WHERE fk_entite = ? AND libelle = ? AND chk_active = 1'; + $params = [$entiteId, $libelle]; + + if ($operationId) { + $sql .= ' AND id != ?'; + $params[] = $operationId; + } + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result && $result['count'] > 0) { + $errors[] = 'Une opération avec ce nom existe déjà dans votre entité'; + } + } + + // Validation des dates + if (!isset($data['date_deb']) || empty($data['date_deb'])) { + $errors[] = 'La date de début est obligatoire'; + } + + if (!isset($data['date_fin']) || empty($data['date_fin'])) { + $errors[] = 'La date de fin est obligatoire'; + } + + if ( + isset($data['date_deb']) && isset($data['date_fin']) && + !empty($data['date_deb']) && !empty($data['date_fin']) + ) { + + $dateDeb = DateTime::createFromFormat('Y-m-d', $data['date_deb']); + $dateFin = DateTime::createFromFormat('Y-m-d', $data['date_fin']); + + if (!$dateDeb) { + $errors[] = 'Format de date de début invalide (YYYY-MM-DD attendu)'; + } + + if (!$dateFin) { + $errors[] = 'Format de date de fin invalide (YYYY-MM-DD attendu)'; + } + + if ($dateDeb && $dateFin && $dateFin <= $dateDeb) { + $errors[] = 'La date de fin doit être postérieure à la date de début'; + } + } + + return empty($errors) ? null : $errors; + } + + /** + * Récupère toutes les opérations de l'entité de l'utilisateur + */ + public function getOperations(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $stmt = $this->db->prepare(' + SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors, + created_at, updated_at, chk_active + FROM operations + WHERE fk_entite = ? + ORDER BY chk_active DESC, created_at DESC + '); + + $stmt->execute([$entiteId]); + $operations = $stmt->fetchAll(PDO::FETCH_ASSOC); + + Response::json([ + 'status' => 'success', + 'operations' => $operations + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des opérations', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des opérations' + ], 500); + } + } + + /** + * Récupère une opération spécifique par son ID + */ + public function getOperationById(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + + $stmt = $this->db->prepare(' + SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors, + created_at, updated_at, chk_active + FROM operations + WHERE id = ? AND fk_entite = ? + '); + + $stmt->execute([$operationId, $entiteId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + Response::json([ + 'status' => 'success', + 'operation' => $operation + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération de l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération de l\'opération' + ], 500); + } + } + + /** + * Crée une nouvelle opération avec duplication des données de l'opération active précédente + */ + public function createOperation(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $data = Request::getJson(); + + // Validation des données + $errors = $this->validateOperationData($data, $entiteId); + if ($errors) { + Response::json([ + 'status' => 'error', + 'message' => 'Erreurs de validation', + 'errors' => $errors + ], 400); + return; + } + + $this->db->beginTransaction(); + + // Étape 1 : Récupérer l'id de l'opération active actuelle (oldOpeId) + $stmt = $this->db->prepare(' + SELECT id FROM operations + WHERE fk_entite = ? AND chk_active = 1 + LIMIT 1 + '); + $stmt->execute([$entiteId]); + $oldOperation = $stmt->fetch(PDO::FETCH_ASSOC); + $oldOpeId = $oldOperation ? (int)$oldOperation['id'] : null; + + LogService::log('Étape 1 : Récupération opération active', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $entiteId, + 'oldOpeId' => $oldOpeId + ]); + + // Étape 2 : Créer la nouvelle opération (newOpeId) - pas encore active + $stmt = $this->db->prepare(' + INSERT INTO operations ( + fk_entite, libelle, date_deb, date_fin, + chk_distinct_sectors, fk_user_creat, chk_active + ) VALUES (?, ?, ?, ?, ?, ?, 0) + '); + + $stmt->execute([ + $entiteId, + trim($data['name']), + $data['date_deb'], + $data['date_fin'], + isset($data['chk_distinct_sectors']) ? (int)$data['chk_distinct_sectors'] : 0, + $userId + ]); + + $newOpeId = (int)$this->db->lastInsertId(); + + LogService::log('Étape 2 : Création nouvelle opération', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $entiteId, + 'newOpeId' => $newOpeId, + 'libelle' => trim($data['name']) + ]); + + // Étape 3 : Insérer tous les users actifs de l'entité dans ope_users avec newOpeId + $stmt = $this->db->prepare(' + INSERT INTO ope_users (fk_operation, fk_user, fk_role, first_name, encrypted_name, sect_name, fk_user_creat) + SELECT ?, id, fk_role, first_name, encrypted_name, sect_name, ? + FROM users + WHERE fk_entite = ? AND chk_active = 1 + '); + $stmt->execute([$newOpeId, $userId, $entiteId]); + $insertedUsers = $stmt->rowCount(); + + LogService::log('Étape 3 : Insertion users actifs', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $entiteId, + 'newOpeId' => $newOpeId, + 'insertedUsers' => $insertedUsers + ]); + + // Étape 4 : Si oldOpeId existe, dupliquer les secteurs et données associées + $duplicatedSectors = 0; + $duplicatedUsersSectors = 0; + $duplicatedPassages = 0; + + if ($oldOpeId) { + // Étape 4.1 : Récupérer tous les secteurs de l'ancienne opération + $stmt = $this->db->prepare(' + SELECT id, libelle, sector, color + FROM ope_sectors + WHERE fk_operation = ? AND chk_active = 1 + '); + $stmt->execute([$oldOpeId]); + $oldSectors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($oldSectors as $oldSector) { + $oldSectId = (int)$oldSector['id']; + + // Étape 4.2 : Dupliquer le secteur avec newOpeId + $stmt = $this->db->prepare(' + INSERT INTO ope_sectors (fk_operation, fk_old_sector, libelle, sector, color, fk_user_creat) + VALUES (?, ?, ?, ?, ?, ?) + '); + $stmt->execute([ + $newOpeId, + $oldSectId, + $oldSector['libelle'], + $oldSector['sector'], + $oldSector['color'], + $userId + ]); + $newSectId = (int)$this->db->lastInsertId(); + $duplicatedSectors++; + + // Étape 4.3 : Dupliquer les users_sectors en vérifiant que fk_user existe dans ope_users + $stmt = $this->db->prepare(' + INSERT INTO ope_users_sectors (fk_operation, fk_user, fk_sector, fk_user_creat) + SELECT ?, ous.fk_user, ?, ? + FROM ope_users_sectors ous + INNER JOIN ope_users ou ON ou.fk_user = ous.fk_user AND ou.fk_operation = ? + WHERE ous.fk_operation = ? AND ous.fk_sector = ? AND ous.chk_active = 1 + '); + $stmt->execute([$newOpeId, $newSectId, $userId, $newOpeId, $oldOpeId, $oldSectId]); + $duplicatedUsersSectors += $stmt->rowCount(); + + // Étape 4.4 : Dupliquer les passages avec les valeurs par défaut spécifiées + $stmt = $this->db->prepare(' + INSERT INTO ope_pass ( + fk_operation, fk_sector, fk_user, fk_adresse, numero, rue, rue_bis, ville, + fk_habitat, appt, niveau, residence, gps_lat, gps_lng, encrypted_name, + fk_type, passed_at, montant, fk_type_reglement, chk_email_sent, chk_striped, + docremis, nb_passages, chk_map_create, chk_mobile, chk_synchro, anomalie, + fk_user_creat, chk_active + ) + SELECT + ?, ?, fk_user, fk_adresse, numero, rue, rue_bis, ville, + fk_habitat, appt, niveau, residence, gps_lat, gps_lng, encrypted_name, + 2, NULL, 0, 4, 0, 0, 0, 1, 0, 0, 1, 0, ?, 1 + FROM ope_pass + WHERE fk_operation = ? AND fk_sector = ? AND chk_active = 1 + '); + $stmt->execute([$newOpeId, $newSectId, $userId, $oldOpeId, $oldSectId]); + $duplicatedPassages += $stmt->rowCount(); + } + + LogService::log('Étape 4 : Duplication données anciennes opération', [ + 'level' => 'info', + 'userId' => $userId, + 'oldOpeId' => $oldOpeId, + 'newOpeId' => $newOpeId, + 'duplicatedSectors' => $duplicatedSectors, + 'duplicatedUsersSectors' => $duplicatedUsersSectors, + 'duplicatedPassages' => $duplicatedPassages + ]); + } + + // Étape 5 : Désactiver l'ancienne opération + if ($oldOpeId) { + $stmt = $this->db->prepare(' + UPDATE operations + SET chk_active = 0, updated_at = NOW(), fk_user_modif = ? + WHERE id = ? + '); + $stmt->execute([$userId, $oldOpeId]); + + LogService::log('Étape 5 : Désactivation ancienne opération', [ + 'level' => 'info', + 'userId' => $userId, + 'oldOpeId' => $oldOpeId + ]); + } + + // Étape 6 : Activer la nouvelle opération + $stmt = $this->db->prepare(' + UPDATE operations + SET chk_active = 1, updated_at = NOW(), fk_user_modif = ? + WHERE id = ? + '); + $stmt->execute([$userId, $newOpeId]); + + LogService::log('Étape 6 : Activation nouvelle opération', [ + 'level' => 'info', + 'userId' => $userId, + 'newOpeId' => $newOpeId + ]); + + $this->db->commit(); + + // Étape 7 : Préparer la réponse avec les groupes JSON + $response = OperationDataService::prepareOperationResponse($this->db, $newOpeId, $entiteId); + + LogService::log('Création opération terminée avec succès', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $entiteId, + 'newOpeId' => $newOpeId, + 'oldOpeId' => $oldOpeId, + 'stats' => [ + 'insertedUsers' => $insertedUsers, + 'duplicatedSectors' => $duplicatedSectors, + 'duplicatedUsersSectors' => $duplicatedUsersSectors, + 'duplicatedPassages' => $duplicatedPassages + ] + ]); + + Response::json($response, 201); + } catch (Exception $e) { + $this->db->rollBack(); + + LogService::log('Erreur lors de la création de l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la création de l\'opération' + ], 500); + } + } + + + /** + * Met à jour une opération (uniquement l'opération active) + */ + public function updateOperation(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + $data = Request::getJson(); + + // Étape 1: Vérifier que l'opération existe (sans filtrer par entité d'abord) + $stmt = $this->db->prepare(' + SELECT id, fk_entite, chk_active + FROM operations + WHERE id = ? + '); + + $stmt->execute([$operationId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + LogService::log('Tentative de mise à jour d\'une opération inexistante', [ + 'level' => 'warning', + 'userId' => $userId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + // Étape 2: Vérifier la cohérence du fk_entite si fourni dans le JSON + if (isset($data['fk_entite'])) { + $receivedEntiteId = (int)$data['fk_entite']; + $operationEntiteId = (int)$operation['fk_entite']; + + if ($receivedEntiteId !== $operationEntiteId) { + LogService::log('Incohérence détectée entre fk_entite reçu et celui de l\'opération', [ + 'level' => 'warning', + 'userId' => $userId, + 'operationId' => $operationId, + 'receivedEntiteId' => $receivedEntiteId, + 'operationEntiteId' => $operationEntiteId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Incohérence détectée : l\'opération n\'appartient pas à l\'entité spécifiée' + ], 400); + return; + } + } + + // Étape 3: Vérifier que l'utilisateur a accès à l'entité de l'opération + $operationEntiteId = (int)$operation['fk_entite']; + if ($operationEntiteId !== $entiteId) { + LogService::log('Tentative d\'accès à une opération d\'une autre entité', [ + 'level' => 'warning', + 'userId' => $userId, + 'userEntiteId' => $entiteId, + 'operationEntiteId' => $operationEntiteId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Vous n\'avez pas accès à cette entité' + ], 403); + return; + } + + // Étape 4: Vérifier que l'opération est active + if (!$operation['chk_active']) { + LogService::log('Tentative de modification d\'une opération inactive', [ + 'level' => 'warning', + 'userId' => $userId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Seule l\'opération active peut être modifiée' + ], 403); + return; + } + + // Validation des données + $errors = $this->validateOperationData($data, $entiteId, $operationId); + if ($errors) { + Response::json([ + 'status' => 'error', + 'message' => 'Erreurs de validation', + 'errors' => $errors + ], 400); + return; + } + + // Mettre à jour l'opération + $stmt = $this->db->prepare(' + UPDATE operations + SET libelle = ?, date_deb = ?, date_fin = ?, + chk_distinct_sectors = ?, updated_at = NOW(), fk_user_modif = ? + WHERE id = ? + '); + + $libelle = trim($data['libelle'] ?? $data['name']); + $stmt->execute([ + $libelle, + $data['date_deb'], + $data['date_fin'], + isset($data['chk_distinct_sectors']) ? (int)$data['chk_distinct_sectors'] : 0, + $userId, + $operationId + ]); + + LogService::log('Mise à jour d\'une opération', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $entiteId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Opération mise à jour avec succès' + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la mise à jour de l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la mise à jour de l\'opération' + ], 500); + } + } + + /** + * Désactive une opération + */ + public function deleteOperation(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $operationId = (int)$id; + + // Récupérer les informations de l'utilisateur (rôle et entité) + $stmt = $this->db->prepare('SELECT fk_entite, fk_role FROM users WHERE id = ?'); + $stmt->execute([$userId]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user) { + Response::json([ + 'status' => 'error', + 'message' => 'Utilisateur non trouvé' + ], 404); + return; + } + + $userEntiteId = (int)$user['fk_entite']; + $userRole = (int)$user['fk_role']; + + // Vérifier que l'utilisateur a un rôle > 1 (pas un simple utilisateur) + if ($userRole <= 1) { + LogService::log('Tentative de suppression d\'opération avec rôle insuffisant', [ + 'level' => 'warning', + 'userId' => $userId, + 'userRole' => $userRole, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Vous n\'avez pas les droits suffisants pour supprimer une opération' + ], 403); + return; + } + + // Récupérer les informations de l'opération + $stmt = $this->db->prepare(' + SELECT id, fk_entite, chk_active + FROM operations + WHERE id = ? + '); + + $stmt->execute([$operationId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + $operationEntiteId = (int)$operation['fk_entite']; + + // Si l'utilisateur a le rôle 2, vérifier qu'il appartient à la même entité que l'opération + if ($userRole == 2 && $userEntiteId !== $operationEntiteId) { + LogService::log('Tentative de suppression d\'opération d\'une autre entité', [ + 'level' => 'warning', + 'userId' => $userId, + 'userRole' => $userRole, + 'userEntiteId' => $userEntiteId, + 'operationEntiteId' => $operationEntiteId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Vous ne pouvez supprimer que les opérations de votre entité' + ], 403); + return; + } + + // Les utilisateurs avec rôle > 2 (super admin, etc.) peuvent supprimer toutes les opérations + // Les utilisateurs avec rôle 2 ne peuvent supprimer que les opérations de leur entité + + $operationActive = (bool)$operation['chk_active']; + + // Créer un export complet automatique avant suppression (Excel + JSON) + try { + $exportService = new ExportService(); + + // Générer l'export Excel + $excelFile = $exportService->generateExcelExport($operationId, $operationEntiteId); + + // Générer l'export JSON + $jsonFile = $exportService->generateJsonExport($operationId, $operationEntiteId, 'auto'); + + LogService::log('Export complet automatique créé avant suppression', [ + 'level' => 'info', + 'userId' => $userId, + 'operationId' => $operationId, + 'operationActive' => $operationActive, + 'excelFile' => $excelFile['filename'], + 'jsonFile' => $jsonFile['filename'] + ]); + } catch (Exception $e) { + LogService::log('Erreur lors de l\'export complet automatique avant suppression', [ + 'level' => 'warning', + 'error' => $e->getMessage(), + 'operationId' => $operationId, + 'operationActive' => $operationActive + ]); + // On continue même si l'export échoue + } + + // Commencer une transaction pour supprimer toutes les données liées + $this->db->beginTransaction(); + + try { + // 1. Supprimer les médias liés à l'opération + $stmt = $this->db->prepare('DELETE FROM medias WHERE support = "operation" AND support_id = ?'); + $stmt->execute([$operationId]); + $deletedMedias = $stmt->rowCount(); + + // 2. Supprimer l'historique des passages (via les passages de l'opération) + $stmt = $this->db->prepare(' + DELETE oph FROM ope_pass_histo oph + INNER JOIN ope_pass op ON oph.fk_pass = op.id + WHERE op.fk_operation = ? + '); + $stmt->execute([$operationId]); + $deletedPassHisto = $stmt->rowCount(); + + // 3. Supprimer les passages + $stmt = $this->db->prepare('DELETE FROM ope_pass WHERE fk_operation = ?'); + $stmt->execute([$operationId]); + $deletedPass = $stmt->rowCount(); + + // 4. Supprimer les relations utilisateurs-secteurs + $stmt = $this->db->prepare('DELETE FROM ope_users_sectors WHERE fk_operation = ?'); + $stmt->execute([$operationId]); + $deletedUsersSectors = $stmt->rowCount(); + + // 5. Supprimer les adresses des secteurs (via les secteurs de l'opération) + $stmt = $this->db->prepare(' + DELETE sa FROM sectors_adresses sa + INNER JOIN ope_sectors os ON sa.fk_sector = os.id + WHERE os.fk_operation = ? + '); + $stmt->execute([$operationId]); + $deletedSectorsAdresses = $stmt->rowCount(); + + // 6. Supprimer les secteurs + $stmt = $this->db->prepare('DELETE FROM ope_sectors WHERE fk_operation = ?'); + $stmt->execute([$operationId]); + $deletedSectors = $stmt->rowCount(); + + // 7. Supprimer les utilisateurs de l'opération + $stmt = $this->db->prepare('DELETE FROM ope_users WHERE fk_operation = ?'); + $stmt->execute([$operationId]); + $deletedUsers = $stmt->rowCount(); + + // 8. Supprimer l'opération elle-même + $stmt = $this->db->prepare('DELETE FROM operations WHERE id = ?'); + $stmt->execute([$operationId]); + + // Valider la transaction + $this->db->commit(); + + LogService::log('Suppression complète d\'une opération et de toutes ses données', [ + 'level' => 'info', + 'userId' => $userId, + 'userRole' => $userRole, + 'userEntiteId' => $userEntiteId, + 'operationEntiteId' => $operationEntiteId, + 'operationId' => $operationId, + 'operationActive' => $operationActive, + 'deletedCounts' => [ + 'medias' => $deletedMedias, + 'ope_pass_histo' => $deletedPassHisto, + 'ope_pass' => $deletedPass, + 'ope_users_sectors' => $deletedUsersSectors, + 'sectors_adresses' => $deletedSectorsAdresses, + 'ope_sectors' => $deletedSectors, + 'ope_users' => $deletedUsers, + 'operations' => 1 + ] + ]); + + // Préparer la réponse selon le statut de l'opération supprimée + $response = [ + 'status' => 'success', + 'message' => 'Opération et toutes ses données supprimées avec succès', + 'operation_was_active' => $operationActive, + 'deleted_counts' => [ + 'medias' => $deletedMedias, + 'passages_history' => $deletedPassHisto, + 'passages' => $deletedPass, + 'user_sectors' => $deletedUsersSectors, + 'sectors_addresses' => $deletedSectorsAdresses, + 'sectors' => $deletedSectors, + 'users' => $deletedUsers + ] + ]; + + // Si l'opération supprimée était active, activer la dernière opération créée + $newActiveOperationId = null; + if ($operationActive) { + // Trouver la dernière opération créée de cette entité + $stmt = $this->db->prepare(' + SELECT id FROM operations + WHERE fk_entite = ? + ORDER BY id DESC + LIMIT 1 + '); + $stmt->execute([$operationEntiteId]); + $lastOperation = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($lastOperation) { + $newActiveOperationId = (int)$lastOperation['id']; + + // Activer cette opération + $stmt = $this->db->prepare(' + UPDATE operations + SET chk_active = 1, updated_at = NOW(), fk_user_modif = ? + WHERE id = ? + '); + $stmt->execute([$userId, $newActiveOperationId]); + + LogService::log('Activation automatique de la dernière opération après suppression', [ + 'level' => 'info', + 'userId' => $userId, + 'entiteId' => $operationEntiteId, + 'newActiveOperationId' => $newActiveOperationId, + 'deletedOperationId' => $operationId + ]); + } + } + + // Récupérer les 3 dernières opérations (dont l'active) + $stmt = $this->db->prepare(' + SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors, + created_at, updated_at, chk_active + FROM operations + WHERE fk_entite = ? + ORDER BY chk_active DESC, created_at DESC + LIMIT 3 + '); + $stmt->execute([$operationEntiteId]); + $operations = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $response['operations'] = $operations; + + // Si une opération a été activée, récupérer ses données complètes + if ($newActiveOperationId) { + // Récupérer les secteurs de la nouvelle opération active + $stmt = $this->db->prepare(' + SELECT id, libelle, color, sector, created_at, updated_at, chk_active + FROM ope_sectors + WHERE fk_operation = ? AND chk_active = 1 + ORDER BY libelle + '); + $stmt->execute([$newActiveOperationId]); + $sectors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Récupérer les passages de la nouvelle opération active + $stmt = $this->db->prepare(' + SELECT + p.id, p.fk_operation, p.fk_sector, p.fk_user, p.fk_adresse, + p.passed_at, p.fk_type, p.numero, p.rue, p.rue_bis, p.ville, + p.fk_habitat, p.appt, p.niveau, p.residence, p.gps_lat, p.gps_lng, + p.encrypted_name, p.montant, p.fk_type_reglement, p.remarque, + p.encrypted_email, p.encrypted_phone, p.nom_recu, p.date_recu, + p.chk_email_sent, p.docremis, p.date_repasser, p.nb_passages, + p.chk_mobile, p.anomalie, p.created_at, p.updated_at, p.chk_active + FROM ope_pass p + WHERE p.fk_operation = ? AND p.chk_active = 1 + ORDER BY p.created_at DESC + LIMIT 50 + '); + $stmt->execute([$newActiveOperationId]); + $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrer les données sensibles des passages + foreach ($passages as &$passage) { + $passage['name'] = ApiService::decryptData($passage['encrypted_name']); + $passage['email'] = !empty($passage['encrypted_email']) ? + ApiService::decryptSearchableData($passage['encrypted_email']) : ''; + $passage['phone'] = !empty($passage['encrypted_phone']) ? + ApiService::decryptData($passage['encrypted_phone']) : ''; + + // Suppression des champs chiffrés + unset($passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone']); + } + + // Récupérer les relations utilisateurs-secteurs + $stmt = $this->db->prepare(' + SELECT + ous.id, ous.fk_operation, ous.fk_user, ous.fk_sector, + ous.created_at, ous.updated_at, ous.chk_active, + u.encrypted_name as user_name, u.first_name as user_first_name, + s.libelle as sector_name + FROM ope_users_sectors ous + INNER JOIN users u ON u.id = ous.fk_user + INNER JOIN ope_sectors s ON s.id = ous.fk_sector + WHERE ous.fk_operation = ? AND ous.chk_active = 1 + ORDER BY s.libelle, u.encrypted_name + '); + $stmt->execute([$newActiveOperationId]); + $usersSectors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrer les noms d'utilisateurs + foreach ($usersSectors as &$userSector) { + $userSector['user_name'] = ApiService::decryptData($userSector['user_name']); + unset($userSector['encrypted_name']); + } + + $response['activated_operation'] = [ + 'id' => $newActiveOperationId, + 'sectors' => $sectors, + 'passages' => $passages, + 'users_sectors' => $usersSectors + ]; + } + + Response::json($response, 200); + } catch (Exception $e) { + // Annuler la transaction en cas d'erreur + $this->db->rollBack(); + + LogService::log('Erreur lors de la suppression complète de l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $operationId, + 'operationActive' => $operationActive, + 'userId' => $userId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la suppression complète de l\'opération' + ], 500); + return; + } + } catch (Exception $e) { + LogService::log('Erreur lors de la suppression de l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la suppression de l\'opération' + ], 500); + } + } + + /** + * Export Excel d'une opération (retourne directement le fichier) + */ + public function exportExcel(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + + // Vérifier d'abord si l'opération existe (sans filtrer par entité) + $stmt = $this->db->prepare(' + SELECT id, libelle, chk_active, fk_entite + FROM operations + WHERE id = ? + '); + + $stmt->execute([$operationId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + LogService::log('Opération inexistante pour export Excel', [ + 'level' => 'warning', + 'operationId' => $operationId, + 'userId' => $userId, + 'entiteId' => $entiteId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + // Vérifier que l'opération appartient à l'entité de l'utilisateur + if ((int)$operation['fk_entite'] !== $entiteId) { + LogService::log('Tentative d\'accès à une opération d\'une autre entité pour export Excel', [ + 'level' => 'warning', + 'operationId' => $operationId, + 'operationEntiteId' => (int)$operation['fk_entite'], + 'userEntiteId' => $entiteId, + 'userId' => $userId + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Vous n\'avez pas accès à cette opération' + ], 403); + return; + } + + // Paramètre optionnel pour filtrer par utilisateur + $filterUserId = isset($_GET['user_id']) ? (int)$_GET['user_id'] : null; + + // Générer l'export Excel + $exportService = new ExportService(); + $fileInfo = $exportService->generateExcelExport($operationId, $entiteId, $filterUserId); + + // Construire le chemin complet du fichier + $filepath = getcwd() . '/' . $fileInfo['path']; + + if (!file_exists($filepath)) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier Excel non trouvé' + ], 404); + return; + } + + // Nettoyer le nom de l'opération pour le nom de fichier + $operationName = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $operation['libelle']); + $userSuffix = $filterUserId ? "-user{$filterUserId}" : ''; + $timestamp = date('Ymd-His'); + $downloadFilename = "export-{$operationName}{$userSuffix}-{$timestamp}.xlsx"; + + // Envoyer le fichier Excel directement + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment; filename="' . $downloadFilename . '"'); + header('Content-Length: ' . filesize($filepath)); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + + // Lire et envoyer le fichier + readfile($filepath); + + LogService::log('Export Excel téléchargé', [ + 'level' => 'info', + 'operationId' => $operationId, + 'entiteId' => $entiteId, + 'userId' => $userId, + 'filename' => $downloadFilename, + 'filterUserId' => $filterUserId + ]); + + exit; + } catch (Exception $e) { + LogService::log('Erreur lors de l\'export Excel', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la génération de l\'export Excel' + ], 500); + } + } + + /** + * Export JSON d'une opération (sauvegarde) + */ + public function exportJson(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + + // Vérifier que l'opération existe et appartient à l'entité + $stmt = $this->db->prepare(' + SELECT id, chk_active + FROM operations + WHERE id = ? AND fk_entite = ? + '); + + $stmt->execute([$operationId, $entiteId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + // Type d'export (manual par défaut) + $exportType = $_GET['type'] ?? 'manual'; + + // Générer l'export JSON + $exportService = new ExportService(); + $fileInfo = $exportService->generateJsonExport($operationId, $entiteId, $exportType); + + Response::json([ + 'status' => 'success', + 'message' => 'Sauvegarde JSON générée avec succès', + 'file' => $fileInfo + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de l\'export JSON', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la génération de la sauvegarde JSON' + ], 500); + } + } + + /** + * Export complet (Excel + JSON) + */ + public function exportFull(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + + // Vérifier que l'opération existe et appartient à l'entité + $stmt = $this->db->prepare(' + SELECT id, chk_active + FROM operations + WHERE id = ? AND fk_entite = ? + '); + + $stmt->execute([$operationId, $entiteId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + $exportService = new ExportService(); + + // Générer les deux exports + $excelFile = $exportService->generateExcelExport($operationId, $entiteId); + $jsonFile = $exportService->generateJsonExport($operationId, $entiteId, 'manual'); + + Response::json([ + 'status' => 'success', + 'message' => 'Export complet généré avec succès', + 'files' => [ + 'excel' => $excelFile, + 'json' => $jsonFile + ] + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de l\'export complet', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la génération de l\'export complet' + ], 500); + } + } + + /** + * Liste des sauvegardes d'une opération + */ + public function getBackups(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + + // Vérifier que l'opération existe et appartient à l'entité + $stmt = $this->db->prepare(' + SELECT id + FROM operations + WHERE id = ? AND fk_entite = ? + '); + + $stmt->execute([$operationId, $entiteId]); + $operation = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$operation) { + Response::json([ + 'status' => 'error', + 'message' => 'Opération non trouvée' + ], 404); + return; + } + + // Récupérer les fichiers d'export de cette opération + $stmt = $this->db->prepare(' + SELECT + id, fichier, file_type, file_size, description, + created_at, fk_user_creat + FROM medias + WHERE support = "operation" AND support_id = ? AND fk_entite = ? + ORDER BY created_at DESC + '); + + $stmt->execute([$operationId, $entiteId]); + $backups = $stmt->fetchAll(PDO::FETCH_ASSOC); + + Response::json([ + 'status' => 'success', + 'backups' => $backups + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des sauvegardes', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des sauvegardes' + ], 500); + } + } + + /** + * Télécharger une sauvegarde spécifique + */ + public function downloadBackup(string $id, string $backup_id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + $backupId = (int)$backup_id; + + // Vérifier que le fichier existe et appartient à l'opération/entité + $stmt = $this->db->prepare(' + SELECT m.fichier, m.file_path, m.mime_type, m.original_name + FROM medias m + INNER JOIN operations o ON o.id = m.support_id + WHERE m.id = ? AND m.support = "operation" AND m.support_id = ? + AND o.fk_entite = ? + '); + + $stmt->execute([$backupId, $operationId, $entiteId]); + $backup = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$backup) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier de sauvegarde non trouvé' + ], 404); + return; + } + + $filepath = getcwd() . '/' . $backup['file_path']; + + if (!file_exists($filepath)) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier physique non trouvé' + ], 404); + return; + } + + // Envoyer le fichier + header('Content-Type: ' . $backup['mime_type']); + header('Content-Disposition: attachment; filename="' . $backup['original_name'] . '"'); + header('Content-Length: ' . filesize($filepath)); + readfile($filepath); + exit; + } catch (Exception $e) { + LogService::log('Erreur lors du téléchargement de sauvegarde', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'backupId' => $backup_id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors du téléchargement' + ], 500); + } + } + + /** + * Supprimer une sauvegarde + */ + public function deleteBackup(string $id, string $backup_id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $operationId = (int)$id; + $backupId = (int)$backup_id; + + // Vérifier que le fichier existe et appartient à l'opération/entité + $stmt = $this->db->prepare(' + SELECT m.id, m.file_path + FROM medias m + INNER JOIN operations o ON o.id = m.support_id + WHERE m.id = ? AND m.support = "operation" AND m.support_id = ? + AND o.fk_entite = ? + '); + + $stmt->execute([$backupId, $operationId, $entiteId]); + $backup = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$backup) { + Response::json([ + 'status' => 'error', + 'message' => 'Fichier de sauvegarde non trouvé' + ], 404); + return; + } + + // Supprimer le fichier physique + $filepath = getcwd() . '/' . $backup['file_path']; + if (file_exists($filepath)) { + unlink($filepath); + } + + // Supprimer l'enregistrement en base + $stmt = $this->db->prepare('DELETE FROM medias WHERE id = ?'); + $stmt->execute([$backupId]); + + LogService::log('Suppression d\'une sauvegarde', [ + 'level' => 'info', + 'userId' => $userId, + 'operationId' => $operationId, + 'backupId' => $backupId + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Sauvegarde supprimée avec succès' + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la suppression de sauvegarde', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $id, + 'backupId' => $backup_id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la suppression' + ], 500); + } + } +} diff --git a/api/src/Controllers/PassageController.php b/api/src/Controllers/PassageController.php new file mode 100644 index 00000000..89a34f9d --- /dev/null +++ b/api/src/Controllers/PassageController.php @@ -0,0 +1,803 @@ +db = Database::getInstance(); + $this->appConfig = AppConfig::getInstance(); + } + + /** + * Récupère l'entité de l'utilisateur connecté + * + * @param int $userId ID de l'utilisateur + * @return int|null ID de l'entité ou null si non trouvé + */ + private function getUserEntiteId(int $userId): ?int { + try { + $stmt = $this->db->prepare('SELECT fk_entite FROM users WHERE id = ?'); + $stmt->execute([$userId]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + return $user ? (int)$user['fk_entite'] : null; + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération de l\'entité utilisateur', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId + ]); + return null; + } + } + + /** + * Vérifie si l'utilisateur a accès à l'opération + * + * @param int $userId ID de l'utilisateur + * @param int $operationId ID de l'opération + * @return bool True si l'utilisateur a accès + */ + private function hasAccessToOperation(int $userId, int $operationId): bool { + try { + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + return false; + } + + $stmt = $this->db->prepare(' + SELECT COUNT(*) as count + FROM operations + WHERE id = ? AND fk_entite = ? AND chk_active = 1 + '); + $stmt->execute([$operationId, $entiteId]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + return $result && $result['count'] > 0; + } catch (Exception $e) { + LogService::log('Erreur lors de la vérification d\'accès à l\'opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId, + 'operationId' => $operationId + ]); + return false; + } + } + + /** + * Valide les données d'un passage + * + * @param array $data Données à valider + * @param int|null $passageId ID du passage (pour update) + * @return array|null Erreurs de validation ou null + */ + private function validatePassageData(array $data, ?int $passageId = null): ?array { + $errors = []; + + // Validation de l'opération + if (!isset($data['fk_operation']) || empty($data['fk_operation'])) { + $errors[] = 'L\'ID de l\'opération est obligatoire'; + } + + // Validation de l'utilisateur + if (!isset($data['fk_user']) || empty($data['fk_user'])) { + $errors[] = 'L\'ID de l\'utilisateur est obligatoire'; + } + + // Validation de l'adresse + if (!isset($data['numero']) || empty(trim($data['numero']))) { + $errors[] = 'Le numéro de rue est obligatoire'; + } + + if (!isset($data['rue']) || empty(trim($data['rue']))) { + $errors[] = 'Le nom de rue est obligatoire'; + } + + if (!isset($data['ville']) || empty(trim($data['ville']))) { + $errors[] = 'La ville est obligatoire'; + } + + // Validation du nom (chiffré) + if (!isset($data['encrypted_name']) && !isset($data['name'])) { + $errors[] = 'Le nom est obligatoire'; + } + + // Validation du montant + if (isset($data['montant'])) { + $montant = (float)$data['montant']; + if ($montant < 0) { + $errors[] = 'Le montant ne peut pas être négatif'; + } + if ($montant > 999999.99) { + $errors[] = 'Le montant ne peut pas dépasser 999999.99'; + } + } + + // Validation de l'email si fourni + if (isset($data['email']) && !empty($data['email'])) { + if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { + $errors[] = 'Format d\'email invalide'; + } + } + + // Validation des coordonnées GPS si fournies + if (isset($data['gps_lat']) && !empty($data['gps_lat'])) { + $lat = (float)$data['gps_lat']; + if ($lat < -90 || $lat > 90) { + $errors[] = 'Latitude invalide (doit être entre -90 et 90)'; + } + } + + if (isset($data['gps_lng']) && !empty($data['gps_lng'])) { + $lng = (float)$data['gps_lng']; + if ($lng < -180 || $lng > 180) { + $errors[] = 'Longitude invalide (doit être entre -180 et 180)'; + } + } + + return empty($errors) ? null : $errors; + } + + /** + * Récupère tous les passages de l'entité de l'utilisateur + */ + public function getPassages(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + // Paramètres de pagination + $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; + $limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 20; + $offset = ($page - 1) * $limit; + + // Filtres optionnels + $operationId = isset($_GET['operation_id']) ? (int)$_GET['operation_id'] : null; + $userId_filter = isset($_GET['user_id']) ? (int)$_GET['user_id'] : null; + + // Construction de la requête + $whereConditions = ['o.fk_entite = ?']; + $params = [$entiteId]; + + if ($operationId) { + $whereConditions[] = 'p.fk_operation = ?'; + $params[] = $operationId; + } + + if ($userId_filter) { + $whereConditions[] = 'p.fk_user = ?'; + $params[] = $userId_filter; + } + + $whereClause = implode(' AND ', $whereConditions); + + // Requête principale avec jointures + $stmt = $this->db->prepare(" + SELECT + p.id, p.fk_operation, p.fk_sector, p.fk_user, p.fk_adresse, + p.passed_at, p.fk_type, p.numero, p.rue, p.rue_bis, p.ville, + p.fk_habitat, p.appt, p.niveau, p.residence, p.gps_lat, p.gps_lng, + p.encrypted_name, p.montant, p.fk_type_reglement, p.remarque, + p.encrypted_email, p.encrypted_phone, p.nom_recu, p.date_recu, + p.chk_email_sent, p.docremis, p.date_repasser, p.nb_passages, + p.chk_mobile, p.anomalie, p.created_at, p.updated_at, p.chk_active, + o.libelle as operation_libelle, + u.encrypted_name as user_name, u.first_name as user_first_name + FROM ope_pass p + INNER JOIN operations o ON p.fk_operation = o.id + INNER JOIN users u ON p.fk_user = u.id + WHERE $whereClause AND p.chk_active = 1 + ORDER BY p.created_at DESC + LIMIT ? OFFSET ? + "); + + $params[] = $limit; + $params[] = $offset; + $stmt->execute($params); + $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrement des données sensibles + foreach ($passages as &$passage) { + $passage['name'] = ApiService::decryptData($passage['encrypted_name']); + $passage['email'] = !empty($passage['encrypted_email']) ? + ApiService::decryptSearchableData($passage['encrypted_email']) : ''; + $passage['phone'] = !empty($passage['encrypted_phone']) ? + ApiService::decryptData($passage['encrypted_phone']) : ''; + $passage['user_name'] = ApiService::decryptData($passage['user_name']); + + // Suppression des champs chiffrés + unset($passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone']); + } + + // Compter le total pour la pagination + $countStmt = $this->db->prepare(" + SELECT COUNT(*) as total + FROM ope_pass p + INNER JOIN operations o ON p.fk_operation = o.id + WHERE $whereClause AND p.chk_active = 1 + "); + $countStmt->execute(array_slice($params, 0, -2)); // Enlever limit et offset + $totalResult = $countStmt->fetch(PDO::FETCH_ASSOC); + $total = $totalResult['total']; + + Response::json([ + 'status' => 'success', + 'passages' => $passages, + 'pagination' => [ + 'page' => $page, + 'limit' => $limit, + 'total' => $total, + 'pages' => ceil($total / $limit) + ] + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des passages', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des passages' + ], 500); + } + } + + /** + * Récupère un passage spécifique par son ID + */ + public function getPassageById(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $passageId = (int)$id; + + $stmt = $this->db->prepare(' + SELECT + p.*, + o.libelle as operation_libelle, + u.encrypted_name as user_name, u.first_name as user_first_name + FROM ope_pass p + INNER JOIN operations o ON p.fk_operation = o.id + INNER JOIN users u ON p.fk_user = u.id + WHERE p.id = ? AND o.fk_entite = ? AND p.chk_active = 1 + '); + + $stmt->execute([$passageId, $entiteId]); + $passage = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$passage) { + Response::json([ + 'status' => 'error', + 'message' => 'Passage non trouvé' + ], 404); + return; + } + + // Déchiffrement des données sensibles + $passage['name'] = ApiService::decryptData($passage['encrypted_name']); + $passage['email'] = !empty($passage['encrypted_email']) ? + ApiService::decryptSearchableData($passage['encrypted_email']) : ''; + $passage['phone'] = !empty($passage['encrypted_phone']) ? + ApiService::decryptData($passage['encrypted_phone']) : ''; + $passage['user_name'] = ApiService::decryptData($passage['user_name']); + + // Suppression des champs chiffrés + unset($passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone']); + + Response::json([ + 'status' => 'success', + 'passage' => $passage + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération du passage', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'passageId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération du passage' + ], 500); + } + } + + /** + * Récupère tous les passages d'une opération spécifique + */ + public function getPassagesByOperation(string $operation_id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $operationId = (int)$operation_id; + + // Vérifier l'accès à l'opération + if (!$this->hasAccessToOperation($userId, $operationId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous n\'avez pas accès à cette opération' + ], 403); + return; + } + + // Paramètres de pagination + $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; + $limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 50; + $offset = ($page - 1) * $limit; + + $stmt = $this->db->prepare(' + SELECT + p.id, p.fk_operation, p.fk_sector, p.fk_user, p.passed_at, + p.numero, p.rue, p.rue_bis, p.ville, p.gps_lat, p.gps_lng, + p.encrypted_name, p.montant, p.fk_type_reglement, p.remarque, + p.encrypted_email, p.encrypted_phone, p.chk_email_sent, + p.docremis, p.date_repasser, p.nb_passages, p.chk_mobile, + p.anomalie, p.created_at, p.updated_at, + u.encrypted_name as user_name, u.first_name as user_first_name + FROM ope_pass p + INNER JOIN users u ON p.fk_user = u.id + WHERE p.fk_operation = ? AND p.chk_active = 1 + ORDER BY p.created_at DESC + LIMIT ? OFFSET ? + '); + + $stmt->execute([$operationId, $limit, $offset]); + $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Déchiffrement des données sensibles + foreach ($passages as &$passage) { + $passage['name'] = ApiService::decryptData($passage['encrypted_name']); + $passage['email'] = !empty($passage['encrypted_email']) ? + ApiService::decryptSearchableData($passage['encrypted_email']) : ''; + $passage['phone'] = !empty($passage['encrypted_phone']) ? + ApiService::decryptData($passage['encrypted_phone']) : ''; + $passage['user_name'] = ApiService::decryptData($passage['user_name']); + + // Suppression des champs chiffrés + unset($passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone']); + } + + // Compter le total + $countStmt = $this->db->prepare(' + SELECT COUNT(*) as total + FROM ope_pass + WHERE fk_operation = ? AND chk_active = 1 + '); + $countStmt->execute([$operationId]); + $totalResult = $countStmt->fetch(PDO::FETCH_ASSOC); + $total = $totalResult['total']; + + Response::json([ + 'status' => 'success', + 'passages' => $passages, + 'pagination' => [ + 'page' => $page, + 'limit' => $limit, + 'total' => $total, + 'pages' => ceil($total / $limit) + ] + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la récupération des passages par opération', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $operation_id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la récupération des passages' + ], 500); + } + } + + /** + * Crée un nouveau passage + */ + public function createPassage(): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $data = Request::getJson(); + + // Validation des données + $errors = $this->validatePassageData($data); + if ($errors) { + Response::json([ + 'status' => 'error', + 'message' => 'Erreurs de validation', + 'errors' => $errors + ], 400); + return; + } + + $operationId = (int)$data['fk_operation']; + + // Vérifier l'accès à l'opération + if (!$this->hasAccessToOperation($userId, $operationId)) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous n\'avez pas accès à cette opération' + ], 403); + return; + } + + // Chiffrement des données sensibles + $encryptedName = isset($data['name']) ? ApiService::encryptData($data['name']) : (isset($data['encrypted_name']) ? $data['encrypted_name'] : ''); + $encryptedEmail = isset($data['email']) && !empty($data['email']) ? + ApiService::encryptSearchableData($data['email']) : ''; + $encryptedPhone = isset($data['phone']) && !empty($data['phone']) ? + ApiService::encryptData($data['phone']) : ''; + + // Préparation des données pour l'insertion + $insertData = [ + 'fk_operation' => $operationId, + 'fk_sector' => isset($data['fk_sector']) ? (int)$data['fk_sector'] : 0, + 'fk_user' => (int)$data['fk_user'], + 'fk_adresse' => $data['fk_adresse'] ?? '', + 'passed_at' => isset($data['passed_at']) ? $data['passed_at'] : null, + 'fk_type' => isset($data['fk_type']) ? (int)$data['fk_type'] : 0, + 'numero' => trim($data['numero']), + 'rue' => trim($data['rue']), + 'rue_bis' => $data['rue_bis'] ?? '', + 'ville' => trim($data['ville']), + 'fk_habitat' => isset($data['fk_habitat']) ? (int)$data['fk_habitat'] : 1, + 'appt' => $data['appt'] ?? '', + 'niveau' => $data['niveau'] ?? '', + 'residence' => $data['residence'] ?? '', + 'gps_lat' => $data['gps_lat'] ?? '', + 'gps_lng' => $data['gps_lng'] ?? '', + 'encrypted_name' => $encryptedName, + 'montant' => isset($data['montant']) ? (float)$data['montant'] : 0.00, + 'fk_type_reglement' => isset($data['fk_type_reglement']) ? (int)$data['fk_type_reglement'] : 1, + 'remarque' => $data['remarque'] ?? '', + 'encrypted_email' => $encryptedEmail, + 'encrypted_phone' => $encryptedPhone, + 'nom_recu' => $data['nom_recu'] ?? null, + 'date_recu' => isset($data['date_recu']) ? $data['date_recu'] : null, + 'docremis' => isset($data['docremis']) ? (int)$data['docremis'] : 0, + 'date_repasser' => isset($data['date_repasser']) ? $data['date_repasser'] : null, + 'nb_passages' => isset($data['nb_passages']) ? (int)$data['nb_passages'] : 1, + 'chk_mobile' => isset($data['chk_mobile']) ? (int)$data['chk_mobile'] : 0, + 'anomalie' => isset($data['anomalie']) ? (int)$data['anomalie'] : 0, + 'fk_user_creat' => $userId + ]; + + // Construction de la requête d'insertion + $fields = array_keys($insertData); + $placeholders = array_fill(0, count($fields), '?'); + + $sql = 'INSERT INTO ope_pass (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $placeholders) . ')'; + + $stmt = $this->db->prepare($sql); + $stmt->execute(array_values($insertData)); + + $passageId = $this->db->lastInsertId(); + + LogService::log('Création d\'un nouveau passage', [ + 'level' => 'info', + 'userId' => $userId, + 'passageId' => $passageId, + 'operationId' => $operationId + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Passage créé avec succès', + 'passage_id' => $passageId + ], 201); + } catch (Exception $e) { + LogService::log('Erreur lors de la création du passage', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la création du passage' + ], 500); + } + } + + /** + * Met à jour un passage existant + */ + public function updatePassage(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $passageId = (int)$id; + $data = Request::getJson(); + + // Vérifier que le passage existe et appartient à l'entité de l'utilisateur + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $stmt = $this->db->prepare(' + SELECT p.id, p.fk_operation + FROM ope_pass p + INNER JOIN operations o ON p.fk_operation = o.id + WHERE p.id = ? AND o.fk_entite = ? AND p.chk_active = 1 + '); + $stmt->execute([$passageId, $entiteId]); + $passage = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$passage) { + Response::json([ + 'status' => 'error', + 'message' => 'Passage non trouvé' + ], 404); + return; + } + + // Validation des données + $errors = $this->validatePassageData($data, $passageId); + if ($errors) { + Response::json([ + 'status' => 'error', + 'message' => 'Erreurs de validation', + 'errors' => $errors + ], 400); + return; + } + + // Construction de la requête de mise à jour dynamique + $updateFields = []; + $params = []; + + // Champs pouvant être mis à jour + $updatableFields = [ + 'fk_sector', + 'fk_user', + 'fk_adresse', + 'passed_at', + 'fk_type', + 'numero', + 'rue', + 'rue_bis', + 'ville', + 'fk_habitat', + 'appt', + 'niveau', + 'residence', + 'gps_lat', + 'gps_lng', + 'montant', + 'fk_type_reglement', + 'remarque', + 'nom_recu', + 'date_recu', + 'docremis', + 'date_repasser', + 'nb_passages', + 'chk_mobile', + 'anomalie' + ]; + + foreach ($updatableFields as $field) { + if (isset($data[$field])) { + $updateFields[] = "$field = ?"; + $params[] = $data[$field]; + } + } + + // Gestion des champs chiffrés + if (isset($data['name'])) { + $updateFields[] = "encrypted_name = ?"; + $params[] = ApiService::encryptData($data['name']); + } + + if (isset($data['email'])) { + $updateFields[] = "encrypted_email = ?"; + $params[] = !empty($data['email']) ? ApiService::encryptSearchableData($data['email']) : ''; + } + + if (isset($data['phone'])) { + $updateFields[] = "encrypted_phone = ?"; + $params[] = !empty($data['phone']) ? ApiService::encryptData($data['phone']) : ''; + } + + if (empty($updateFields)) { + Response::json([ + 'status' => 'error', + 'message' => 'Aucune donnée à mettre à jour' + ], 400); + return; + } + + // Ajout des champs de mise à jour + $updateFields[] = "updated_at = NOW()"; + $updateFields[] = "fk_user_modif = ?"; + $params[] = $userId; + $params[] = $passageId; + + $sql = 'UPDATE ope_pass SET ' . implode(', ', $updateFields) . ' WHERE id = ?'; + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + + LogService::log('Mise à jour d\'un passage', [ + 'level' => 'info', + 'userId' => $userId, + 'passageId' => $passageId + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Passage mis à jour avec succès' + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la mise à jour du passage', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'passageId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la mise à jour du passage' + ], 500); + } + } + + /** + * Supprime (désactive) un passage + */ + public function deletePassage(string $id): void { + try { + $userId = Session::getUserId(); + if (!$userId) { + Response::json([ + 'status' => 'error', + 'message' => 'Vous devez être connecté pour effectuer cette action' + ], 401); + return; + } + + $passageId = (int)$id; + + // Vérifier que le passage existe et appartient à l'entité de l'utilisateur + $entiteId = $this->getUserEntiteId($userId); + if (!$entiteId) { + Response::json([ + 'status' => 'error', + 'message' => 'Entité non trouvée pour cet utilisateur' + ], 404); + return; + } + + $stmt = $this->db->prepare(' + SELECT p.id + FROM ope_pass p + INNER JOIN operations o ON p.fk_operation = o.id + WHERE p.id = ? AND o.fk_entite = ? AND p.chk_active = 1 + '); + $stmt->execute([$passageId, $entiteId]); + $passage = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$passage) { + Response::json([ + 'status' => 'error', + 'message' => 'Passage non trouvé' + ], 404); + return; + } + + // Désactiver le passage (soft delete) + $stmt = $this->db->prepare(' + UPDATE ope_pass + SET chk_active = 0, updated_at = NOW(), fk_user_modif = ? + WHERE id = ? + '); + + $stmt->execute([$userId, $passageId]); + + LogService::log('Suppression d\'un passage', [ + 'level' => 'info', + 'userId' => $userId, + 'passageId' => $passageId + ]); + + Response::json([ + 'status' => 'success', + 'message' => 'Passage supprimé avec succès' + ], 200); + } catch (Exception $e) { + LogService::log('Erreur lors de la suppression du passage', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'passageId' => $id, + 'userId' => $userId ?? null + ]); + + Response::json([ + 'status' => 'error', + 'message' => 'Erreur lors de la suppression du passage' + ], 500); + } + } +} diff --git a/api/src/Core/Router.php b/api/src/Core/Router.php index e514f4d6..dd9fbd4e 100644 --- a/api/src/Core/Router.php +++ b/api/src/Core/Router.php @@ -45,8 +45,41 @@ class Router { $this->get('entites/postal/:code', ['EntiteController', 'getEntiteByPostalCode']); $this->put('entites/:id', ['EntiteController', 'updateEntite']); + // Routes opérations + $this->get('operations', ['OperationController', 'getOperations']); + $this->get('operations/:id', ['OperationController', 'getOperationById']); + $this->post('operations', ['OperationController', 'createOperation']); + $this->put('operations/:id', ['OperationController', 'updateOperation']); + $this->delete('operations/:id', ['OperationController', 'deleteOperation']); + + // Routes d'export d'opérations + $this->get('operations/:id/export/excel', ['OperationController', 'exportExcel']); + $this->get('operations/:id/export/json', ['OperationController', 'exportJson']); + $this->get('operations/:id/export/full', ['OperationController', 'exportFull']); + $this->get('operations/:id/backups', ['OperationController', 'getBackups']); + $this->get('operations/:id/backups/:backup_id', ['OperationController', 'downloadBackup']); + $this->delete('operations/:id/backups/:backup_id', ['OperationController', 'deleteBackup']); + + // Routes passages + $this->get('passages', ['PassageController', 'getPassages']); + $this->get('passages/:id', ['PassageController', 'getPassageById']); + $this->get('passages/operation/:operation_id', ['PassageController', 'getPassagesByOperation']); + $this->post('passages', ['PassageController', 'createPassage']); + $this->put('passages/:id', ['PassageController', 'updatePassage']); + $this->delete('passages/:id', ['PassageController', 'deletePassage']); + // Routes villes $this->get('villes', ['VilleController', 'searchVillesByPostalCode']); + + // Routes fichiers + $this->get('files/browse', ['FileController', 'browse']); + $this->get('files/search', ['FileController', 'search']); + $this->get('files/stats', ['FileController', 'getStats']); + $this->get('files/metadata', ['FileController', 'getMetadata']); + $this->get('files/list/:support/:id', ['FileController', 'listBySupport']); + $this->get('files/info/:id', ['FileController', 'getFileInfo']); + $this->get('files/download/:id', ['FileController', 'download']); + $this->delete('files/:id', ['FileController', 'deleteFile']); } public function handle(): void { diff --git a/api/src/Services/BackupEncryptionService.php b/api/src/Services/BackupEncryptionService.php new file mode 100644 index 00000000..3600a842 --- /dev/null +++ b/api/src/Services/BackupEncryptionService.php @@ -0,0 +1,304 @@ +appConfig = AppConfig::getInstance(); + $this->encryptionKey = $this->appConfig->getBackupEncryptionKey(); + $this->backupConfig = $this->appConfig->getBackupConfig(); + } + + /** + * Chiffre et compresse les données JSON d'un backup + * + * @param string $jsonData Données JSON à sauvegarder + * @return string Données chiffrées et compressées en base64 + * @throws Exception En cas d'erreur de compression ou chiffrement + */ + public function encryptBackup(string $jsonData): string { + try { + // Étape 1: Compression GZIP si activée + $dataToEncrypt = $jsonData; + if ($this->backupConfig['compression']) { + $compressed = gzencode($jsonData, $this->backupConfig['compression_level']); + if ($compressed === false) { + throw new Exception('Erreur lors de la compression GZIP'); + } + $dataToEncrypt = $compressed; + + LogService::log('Compression backup réussie', [ + 'level' => 'debug', + 'original_size' => strlen($jsonData), + 'compressed_size' => strlen($compressed), + 'compression_ratio' => round((1 - strlen($compressed) / strlen($jsonData)) * 100, 2) . '%' + ]); + } + + // Étape 2: Génération d'un IV aléatoire pour AES-256-CBC + $ivLength = openssl_cipher_iv_length($this->backupConfig['cipher']); + $iv = openssl_random_pseudo_bytes($ivLength); + + if ($iv === false || strlen($iv) !== $ivLength) { + throw new Exception('Erreur lors de la génération de l\'IV'); + } + + // Étape 3: Chiffrement AES-256-CBC + $encrypted = openssl_encrypt( + $dataToEncrypt, + $this->backupConfig['cipher'], + $this->encryptionKey, + OPENSSL_RAW_DATA, + $iv + ); + + if ($encrypted === false) { + throw new Exception('Erreur lors du chiffrement AES-256'); + } + + // Étape 4: Concaténation IV + données chiffrées et encodage base64 + $finalData = base64_encode($iv . $encrypted); + + LogService::log('Chiffrement backup réussi', [ + 'level' => 'debug', + 'data_size' => strlen($dataToEncrypt), + 'encrypted_size' => strlen($finalData), + 'cipher' => $this->backupConfig['cipher'] + ]); + + return $finalData; + } catch (Exception $e) { + LogService::log('Erreur lors du chiffrement du backup', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'data_size' => strlen($jsonData) + ]); + throw $e; + } + } + + /** + * Déchiffre et décompresse les données d'un backup + * + * @param string $encryptedData Données chiffrées en base64 + * @return string Données JSON originales + * @throws Exception En cas d'erreur de déchiffrement ou décompression + */ + public function decryptBackup(string $encryptedData): string { + try { + // Étape 1: Décodage base64 + $rawData = base64_decode($encryptedData); + if ($rawData === false) { + throw new Exception('Erreur lors du décodage base64'); + } + + // Étape 2: Extraction de l'IV + $ivLength = openssl_cipher_iv_length($this->backupConfig['cipher']); + if (strlen($rawData) < $ivLength) { + throw new Exception('Données corrompues : taille insuffisante pour l\'IV'); + } + + $iv = substr($rawData, 0, $ivLength); + $encryptedContent = substr($rawData, $ivLength); + + // Étape 3: Déchiffrement AES-256-CBC + $decrypted = openssl_decrypt( + $encryptedContent, + $this->backupConfig['cipher'], + $this->encryptionKey, + OPENSSL_RAW_DATA, + $iv + ); + + if ($decrypted === false) { + throw new Exception('Erreur lors du déchiffrement : clé invalide ou données corrompues'); + } + + LogService::log('Déchiffrement backup réussi', [ + 'level' => 'debug', + 'encrypted_size' => strlen($encryptedData), + 'decrypted_size' => strlen($decrypted) + ]); + + // Étape 4: Décompression GZIP si les données sont compressées + $finalData = $decrypted; + if ($this->backupConfig['compression']) { + // Vérifier si les données sont bien compressées (magic number GZIP) + if (substr($decrypted, 0, 2) === "\x1f\x8b") { + $decompressed = gzdecode($decrypted); + if ($decompressed === false) { + throw new Exception('Erreur lors de la décompression GZIP'); + } + $finalData = $decompressed; + + LogService::log('Décompression backup réussie', [ + 'level' => 'debug', + 'compressed_size' => strlen($decrypted), + 'decompressed_size' => strlen($decompressed) + ]); + } + } + + // Étape 5: Validation que le résultat est du JSON valide + $jsonTest = json_decode($finalData, true); + if ($jsonTest === null && json_last_error() !== JSON_ERROR_NONE) { + throw new Exception('Les données déchiffrées ne sont pas du JSON valide : ' . json_last_error_msg()); + } + + return $finalData; + } catch (Exception $e) { + LogService::log('Erreur lors du déchiffrement du backup', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'encrypted_size' => strlen($encryptedData) + ]); + throw $e; + } + } + + /** + * Vérifie si un fichier de backup est chiffré + * + * @param string $filePath Chemin vers le fichier + * @return bool True si le fichier est chiffré + */ + public function isEncryptedBackup(string $filePath): bool { + return str_ends_with($filePath, '.json.gz.enc') || str_ends_with($filePath, '.enc'); + } + + /** + * Vérifie si un fichier de backup est compressé (mais pas chiffré) + * + * @param string $filePath Chemin vers le fichier + * @return bool True si le fichier est compressé + */ + public function isCompressedBackup(string $filePath): bool { + return str_ends_with($filePath, '.json.gz') && !str_ends_with($filePath, '.enc'); + } + + /** + * Lit un fichier de backup en détectant automatiquement le format + * + * @param string $filePath Chemin vers le fichier de backup + * @return array Données JSON décodées + * @throws Exception En cas d'erreur de lecture ou format non supporté + */ + public function readBackupFile(string $filePath): array { + if (!file_exists($filePath)) { + throw new Exception("Fichier de backup non trouvé : {$filePath}"); + } + + $fileContent = file_get_contents($filePath); + if ($fileContent === false) { + throw new Exception("Impossible de lire le fichier : {$filePath}"); + } + + try { + // Fichier chiffré + if ($this->isEncryptedBackup($filePath)) { + $jsonContent = $this->decryptBackup($fileContent); + } + // Fichier compressé seulement + elseif ($this->isCompressedBackup($filePath)) { + $jsonContent = gzdecode($fileContent); + if ($jsonContent === false) { + throw new Exception('Erreur lors de la décompression du fichier'); + } + } + // Fichier JSON brut + else { + $jsonContent = $fileContent; + } + + // Décodage JSON + $data = json_decode($jsonContent, true); + if ($data === null && json_last_error() !== JSON_ERROR_NONE) { + throw new Exception('JSON invalide : ' . json_last_error_msg()); + } + + LogService::log('Lecture backup réussie', [ + 'level' => 'info', + 'file_path' => $filePath, + 'file_size' => filesize($filePath), + 'is_encrypted' => $this->isEncryptedBackup($filePath), + 'is_compressed' => $this->isCompressedBackup($filePath) + ]); + + return $data; + } catch (Exception $e) { + LogService::log('Erreur lors de la lecture du backup', [ + 'level' => 'error', + 'file_path' => $filePath, + 'error' => $e->getMessage() + ]); + throw $e; + } + } + + /** + * Génère le nom de fichier approprié selon la configuration + * + * @param int $operationId ID de l'opération + * @param string $timestamp Timestamp pour l'unicité + * @param string $type Type d'export (manual, auto, etc.) + * @return string Nom du fichier avec extension appropriée + */ + public function generateBackupFilename(int $operationId, string $timestamp, string $type = 'manual'): string { + $baseName = "backup_operation_{$operationId}_{$timestamp}"; + + if ($type !== 'manual') { + $baseName .= "_{$type}"; + } + + // Extension selon la configuration + $extension = '.json'; + + if ($this->backupConfig['compression']) { + $extension .= '.gz'; + } + + // Toujours chiffré + $extension .= '.enc'; + + return $baseName . $extension; + } + + /** + * Retourne les statistiques de compression et chiffrement + * + * @param string $originalJson JSON original + * @param string $finalData Données finales chiffrées + * @return array Statistiques détaillées + */ + public function getCompressionStats(string $originalJson, string $finalData): array { + $originalSize = strlen($originalJson); + $finalSize = strlen($finalData); + + return [ + 'original_size' => $originalSize, + 'final_size' => $finalSize, + 'size_reduction' => $originalSize - $finalSize, + 'compression_ratio' => $originalSize > 0 ? round((1 - $finalSize / $originalSize) * 100, 2) : 0, + 'is_compressed' => $this->backupConfig['compression'], + 'is_encrypted' => true, + 'cipher' => $this->backupConfig['cipher'] + ]; + } +} diff --git a/api/src/Services/ExportService.php b/api/src/Services/ExportService.php new file mode 100644 index 00000000..053ca94f --- /dev/null +++ b/api/src/Services/ExportService.php @@ -0,0 +1,933 @@ +db = Database::getInstance(); + $this->fileService = new FileService(); + } + + /** + * Génère un export Excel complet d'une opération + * + * @param int $operationId ID de l'opération + * @param int $entiteId ID de l'entité + * @param int|null $userId Filtrer par utilisateur (optionnel) + * @return array Informations du fichier généré + */ + public function generateExcelExport(int $operationId, int $entiteId, ?int $userId = null): array { + try { + // Récupérer les données de l'opération + $operationData = $this->getOperationData($operationId, $entiteId); + if (!$operationData) { + throw new Exception('Opération non trouvée'); + } + + // Créer le dossier de destination + $exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/excel"); + + LogService::log('exportDir', [ + 'level' => 'warning', + 'exportDir' => $exportDir, + ]); + + // Générer le nom du fichier + $timestamp = date('Ymd-His'); + $userSuffix = $userId ? "-user{$userId}" : ''; + $filename = "geosector-export-{$operationId}{$userSuffix}-{$timestamp}.xlsx"; + $filepath = $exportDir . '/' . $filename; + + // Créer le spreadsheet + $spreadsheet = new PhpOffice\PhpSpreadsheet\Spreadsheet(); + + // Insérer les données + $this->createPassagesSheet($spreadsheet, $operationId, $userId); + $this->createUsersSheet($spreadsheet, $operationId); + $this->createSectorsSheet($spreadsheet, $operationId); + $this->createUserSectorsSheet($spreadsheet, $operationId); + + // Supprimer la feuille par défaut (Worksheet) qui est créée automatiquement + $defaultSheet = $spreadsheet->getSheetByName('Worksheet'); + if ($defaultSheet) { + $spreadsheet->removeSheetByIndex($spreadsheet->getIndex($defaultSheet)); + } + + // Essayer d'abord le writer XLSX, sinon utiliser CSV + try { + $writer = new Xls($spreadsheet); + $writer->save($filepath); + } catch (Exception $e) { + // Si XLSX échoue, utiliser CSV comme fallback + $csvPath = str_replace('.xlsx', '.csv', $filepath); + $csvWriter = new Csv($spreadsheet); + $csvWriter->setDelimiter(';'); + $csvWriter->setEnclosure('"'); + $csvWriter->save($csvPath); + + // Mettre à jour les variables pour le CSV + $filepath = $csvPath; + $filename = str_replace('.xlsx', '.csv', $filename); + + LogService::log('Fallback vers CSV car XLSX a échoué', [ + 'level' => 'warning', + 'error' => $e->getMessage(), + 'operationId' => $operationId + ]); + } + + // Appliquer les permissions sur le fichier + $this->fileService->setFilePermissions($filepath); + + // Déterminer le type de fichier réellement généré + $fileType = str_ends_with($filename, '.csv') ? 'csv' : 'xlsx'; + + // Enregistrer en base de données + $mediaId = $this->fileService->saveToMediasTable($entiteId, $operationId, $filename, $filepath, $fileType, 'Export Excel opération - ' . $operationData['libelle']); + + LogService::log('Export Excel généré', [ + 'level' => 'info', + 'operationId' => $operationId, + 'entiteId' => $entiteId, + 'path' => $exportDir, + 'filename' => $filename, + 'mediaId' => $mediaId + ]); + + return [ + 'id' => $mediaId, + 'filename' => $filename, + 'path' => str_replace(getcwd() . '/', '', $filepath), + 'size' => filesize($filepath), + 'type' => 'excel' + ]; + } catch (Exception $e) { + LogService::log('Erreur lors de la génération de l\'export Excel', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $operationId, + 'entiteId' => $entiteId + ]); + throw $e; + } + } + + /** + * Génère un export JSON complet d'une opération (chiffré et compressé) + * + * @param int $operationId ID de l'opération + * @param int $entiteId ID de l'entité + * @param string $type Type d'export (auto, manual) + * @return array Informations du fichier généré + */ + public function generateJsonExport(int $operationId, int $entiteId, string $type = 'manual'): array { + try { + // Récupérer toutes les données de l'opération + $exportData = $this->collectOperationData($operationId, $entiteId); + + // Créer le dossier de destination + $exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/json"); + + // Initialiser le service de chiffrement + $backupService = new BackupEncryptionService(); + + // Générer le JSON original + $jsonData = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + + // Chiffrer et compresser les données + $encryptedData = $backupService->encryptBackup($jsonData); + + // Générer le nom du fichier avec extension appropriée + $timestamp = date('Ymd-His'); + $filename = $backupService->generateBackupFilename($operationId, $timestamp, $type); + $filepath = $exportDir . '/' . $filename; + + // Sauvegarder le fichier chiffré + file_put_contents($filepath, $encryptedData); + + // Appliquer les permissions sur le fichier + $this->fileService->setFilePermissions($filepath); + + // Obtenir les statistiques de compression + $stats = $backupService->getCompressionStats($jsonData, $encryptedData); + + // Enregistrer en base de données avec le bon type MIME + $mediaId = $this->fileService->saveToMediasTable( + $entiteId, + $operationId, + $filename, + $filepath, + 'enc', + "Sauvegarde chiffrée opération - {$type} - " . $exportData['operation']['libelle'], + 'backup' + ); + + LogService::log('Export JSON chiffré généré', [ + 'level' => 'info', + 'operationId' => $operationId, + 'entiteId' => $entiteId, + 'filename' => $filename, + 'type' => $type, + 'mediaId' => $mediaId, + 'original_size' => $stats['original_size'], + 'final_size' => $stats['final_size'], + 'compression_ratio' => $stats['compression_ratio'] . '%', + 'is_compressed' => $stats['is_compressed'], + 'cipher' => $stats['cipher'] + ]); + + return [ + 'id' => $mediaId, + 'filename' => $filename, + 'path' => str_replace(getcwd() . '/', '', $filepath), + 'size' => filesize($filepath), + 'type' => 'encrypted_json', + 'compression_stats' => $stats + ]; + } catch (Exception $e) { + LogService::log('Erreur lors de la génération de l\'export JSON chiffré', [ + 'level' => 'error', + 'error' => $e->getMessage(), + 'operationId' => $operationId, + 'entiteId' => $entiteId + ]); + throw $e; + } + } + + /** + * Crée la feuille des passages + */ + private function createPassagesSheet(Spreadsheet $spreadsheet, int $operationId, ?int $userId = null): void { + $sheet = $spreadsheet->createSheet(); + $sheet->setTitle('Passages'); + + // En-têtes + $headers = [ + 'ID_Passage', + 'Date', + 'Heure', + 'Prénom', + 'Nom', + 'Tournée', + 'Type', + 'N°', + 'Bis', + 'Rue', + 'Ville', + 'Habitat', + 'Donateur', + 'Email', + 'Tél', + 'Montant', + 'Règlement', + 'Remarque', + 'FK_User', + 'FK_Sector', + 'FK_Operation' + ]; + + // Écrire les en-têtes + $sheet->fromArray([$headers], null, 'A1'); + + // Récupérer les données des passages + $sql = ' + SELECT + p.id, p.passed_at, p.fk_type, p.numero, p.rue_bis, p.rue, p.ville, + p.fk_habitat, p.appt, p.niveau, p.encrypted_name, p.encrypted_email, + p.encrypted_phone, p.montant, p.fk_type_reglement, p.remarque, + p.fk_user, p.fk_sector, p.fk_operation, + u.encrypted_name as user_name, u.first_name as user_first_name, u.sect_name, + xtr.libelle as reglement_libelle + FROM ope_pass p + LEFT JOIN users u ON u.id = p.fk_user + LEFT JOIN x_types_reglements xtr ON xtr.id = p.fk_type_reglement + WHERE p.fk_operation = ? AND p.chk_active = 1 + '; + + $params = [$operationId]; + if ($userId) { + $sql .= ' AND p.fk_user = ?'; + $params[] = $userId; + } + + $sql .= ' ORDER BY p.passed_at DESC'; + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Remplir les données + $row = 2; + foreach ($passages as $passage) { + $dateEve = $passage['passed_at'] ? date('d/m/Y', strtotime($passage['passed_at'])) : ''; + $heureEve = $passage['passed_at'] ? date('H:i', strtotime($passage['passed_at'])) : ''; + + // Déchiffrer les données + $donateur = ApiService::decryptData($passage['encrypted_name']); + $email = !empty($passage['encrypted_email']) ? ApiService::decryptSearchableData($passage['encrypted_email']) : ''; + $phone = !empty($passage['encrypted_phone']) ? ApiService::decryptData($passage['encrypted_phone']) : ''; + $userName = ApiService::decryptData($passage['user_name']); + + // Type de passage + $typeLabels = [ + 1 => 'Effectué', + 2 => 'A finaliser', + 3 => 'Refusé', + 4 => 'Don', + 9 => 'Habitat vide' + ]; + $typeLabel = $typeLabels[$passage['fk_type']] ?? $passage['fk_type']; + + // Habitat + $habitat = $passage['fk_habitat'] == 1 ? 'Individuel' : + "Etage {$passage['niveau']} - Appt {$passage['appt']}"; + + $rowData = [ + $passage['id'], + $dateEve, + $heureEve, + $passage['user_first_name'], + $userName, + $passage['sect_name'], + $typeLabel, + $passage['numero'], + $passage['rue_bis'], + $passage['rue'], + $passage['ville'], + $habitat, + $donateur, + $email, + $phone, + $passage['montant'], + $passage['reglement_libelle'], + $passage['remarque'], + $passage['fk_user'], + $passage['fk_sector'], + $passage['fk_operation'] + ]; + + $sheet->fromArray([$rowData], null, "A{$row}"); + $row++; + } + + // Auto-ajuster les colonnes + foreach (range('A', 'T') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + } + + /** + * Crée la feuille des utilisateurs + */ + private function createUsersSheet(Spreadsheet $spreadsheet, int $operationId): void { + $sheet = $spreadsheet->createSheet(); + $sheet->setTitle('Utilisateurs'); + + // En-têtes + $headers = [ + 'ID_User', + 'Nom', + 'Prénom', + 'Email', + 'Téléphone', + 'Mobile', + 'Rôle', + 'Date_création', + 'Actif', + 'FK_Entite' + ]; + + $sheet->fromArray([$headers], null, 'A1'); + + // Récupérer les utilisateurs de l'opération + $sql = ' + SELECT DISTINCT + u.id, u.encrypted_name, u.first_name, u.encrypted_email, + u.encrypted_phone, u.encrypted_mobile, u.fk_role, u.created_at, + u.chk_active, u.fk_entite, + r.libelle as role_libelle + FROM users u + INNER JOIN ope_users ou ON ou.fk_user = u.id + LEFT JOIN x_users_roles r ON r.id = u.fk_role + WHERE ou.fk_operation = ? AND ou.chk_active = 1 + ORDER BY u.encrypted_name + '; + + $stmt = $this->db->prepare($sql); + $stmt->execute([$operationId]); + $users = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $row = 2; + foreach ($users as $user) { + $rowData = [ + $user['id'], + ApiService::decryptData($user['encrypted_name']), + $user['first_name'], + !empty($user['encrypted_email']) ? ApiService::decryptSearchableData($user['encrypted_email']) : '', + !empty($user['encrypted_phone']) ? ApiService::decryptData($user['encrypted_phone']) : '', + !empty($user['encrypted_mobile']) ? ApiService::decryptData($user['encrypted_mobile']) : '', + $user['role_libelle'], + date('d/m/Y H:i', strtotime($user['created_at'])), + $user['chk_active'] ? 'Oui' : 'Non', + $user['fk_entite'] + ]; + + $sheet->fromArray([$rowData], null, "A{$row}"); + $row++; + } + + foreach (range('A', 'J') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + } + + /** + * Crée la feuille des secteurs + */ + private function createSectorsSheet(Spreadsheet $spreadsheet, int $operationId): void { + $sheet = $spreadsheet->createSheet(); + $sheet->setTitle('Secteurs'); + + $headers = ['ID_Sector', 'Libellé', 'Couleur', 'Date_création', 'Actif', 'FK_Operation']; + $sheet->fromArray([$headers], null, 'A1'); + + $sql = ' + SELECT id, libelle, color, created_at, chk_active, fk_operation + FROM ope_sectors + WHERE fk_operation = ? AND chk_active = 1 + ORDER BY libelle + '; + + $stmt = $this->db->prepare($sql); + $stmt->execute([$operationId]); + $sectors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $row = 2; + foreach ($sectors as $sector) { + $rowData = [ + $sector['id'], + $sector['libelle'], + $sector['color'], + date('d/m/Y H:i', strtotime($sector['created_at'])), + $sector['chk_active'] ? 'Oui' : 'Non', + $sector['fk_operation'] + ]; + + $sheet->fromArray([$rowData], null, "A{$row}"); + $row++; + } + + foreach (range('A', 'F') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + } + + /** + * Crée la feuille des relations secteurs-utilisateurs + */ + private function createUserSectorsSheet(Spreadsheet $spreadsheet, int $operationId): void { + $sheet = $spreadsheet->createSheet(); + $sheet->setTitle('Secteurs-Utilisateurs'); + + $headers = [ + 'ID_Relation', + 'FK_Sector', + 'Nom_Secteur', + 'FK_User', + 'Nom_Utilisateur', + 'Date_assignation', + 'FK_Operation' + ]; + $sheet->fromArray([$headers], null, 'A1'); + + $sql = ' + SELECT + ous.id, ous.fk_sector, ous.fk_user, ous.created_at, ous.fk_operation, + s.libelle as sector_name, + u.encrypted_name as user_name, u.first_name + FROM ope_users_sectors ous + INNER JOIN ope_sectors s ON s.id = ous.fk_sector + INNER JOIN users u ON u.id = ous.fk_user + WHERE ous.fk_operation = ? AND ous.chk_active = 1 + ORDER BY s.libelle, u.encrypted_name + '; + + $stmt = $this->db->prepare($sql); + $stmt->execute([$operationId]); + $userSectors = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $row = 2; + foreach ($userSectors as $us) { + $userName = ApiService::decryptData($us['user_name']); + $fullUserName = $us['first_name'] ? $us['first_name'] . ' ' . $userName : $userName; + + $rowData = [ + $us['id'], + $us['fk_sector'], + $us['sector_name'], + $us['fk_user'], + $fullUserName, + date('d/m/Y H:i', strtotime($us['created_at'])), + $us['fk_operation'] + ]; + + $sheet->fromArray([$rowData], null, "A{$row}"); + $row++; + } + + foreach (range('A', 'G') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + } + + /** + * Collecte toutes les données d'une opération pour l'export JSON + */ + private function collectOperationData(int $operationId, int $entiteId): array { + // Métadonnées de l'export + $exportData = [ + 'export_metadata' => [ + 'version' => '1.0', + 'export_date' => date('c'), + 'source_entite_id' => $entiteId, + 'export_type' => 'full_operation' + ] + ]; + + // Données de l'opération + $exportData['operation'] = $this->getOperationData($operationId, $entiteId); + + // Utilisateurs de l'opération + $exportData['users'] = $this->getOperationUsers($operationId); + + // Secteurs de l'opération + $exportData['sectors'] = $this->getOperationSectors($operationId); + + // Passages de l'opération + $exportData['passages'] = $this->getOperationPassages($operationId); + + // Relations utilisateurs-secteurs + $exportData['user_sectors'] = $this->getOperationUserSectors($operationId); + + return $exportData; + } + + /** + * Récupère les données de l'opération + */ + private function getOperationData(int $operationId, int $entiteId): ?array { + $stmt = $this->db->prepare(' + SELECT * FROM operations + WHERE id = ? AND fk_entite = ? + '); + $stmt->execute([$operationId, $entiteId]); + return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; + } + + /** + * Récupère les utilisateurs de l'opération + */ + private function getOperationUsers(int $operationId): array { + $stmt = $this->db->prepare(' + SELECT DISTINCT u.* + FROM users u + INNER JOIN ope_users ou ON ou.fk_user = u.id + WHERE ou.fk_operation = ? AND ou.chk_active = 1 + '); + $stmt->execute([$operationId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Récupère les secteurs de l'opération + */ + private function getOperationSectors(int $operationId): array { + $stmt = $this->db->prepare(' + SELECT * FROM ope_sectors + WHERE fk_operation = ? AND chk_active = 1 + '); + $stmt->execute([$operationId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Récupère les passages de l'opération + */ + private function getOperationPassages(int $operationId): array { + $stmt = $this->db->prepare(' + SELECT * FROM ope_pass + WHERE fk_operation = ? AND chk_active = 1 + '); + $stmt->execute([$operationId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Récupère les relations utilisateurs-secteurs + */ + private function getOperationUserSectors(int $operationId): array { + $stmt = $this->db->prepare(' + SELECT * FROM ope_users_sectors + WHERE fk_operation = ? AND chk_active = 1 + '); + $stmt->execute([$operationId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Récupère les données des passages dans un format simple pour l'export Excel + * (inspiré de l'ancienne version qui fonctionne) + */ + private function getSimplePassagesData(int $operationId, ?int $userId = null): array { + // En-têtes (comme dans l'ancienne version) + $aData = []; + $aData[] = [ + 'Date', + 'Heure', + 'Prenom', + 'Nom', + 'Tournee', + 'Type', + 'N°', + 'Rue', + 'Ville', + 'Habitat', + 'Donateur', + 'Email', + 'Tel', + 'Montant', + 'Reglement', + 'Remarque' + ]; + + // Récupérer les données des passages + $sql = ' + SELECT + p.passed_at, p.fk_type, p.numero, p.rue_bis, p.rue, p.ville, + p.fk_habitat, p.appt, p.niveau, p.encrypted_name, p.encrypted_email, + p.encrypted_phone, p.montant, p.fk_type_reglement, p.remarque, + u.encrypted_name as user_name, u.first_name as user_first_name, u.sect_name, + xtr.libelle as reglement_libelle + FROM ope_pass p + LEFT JOIN users u ON u.id = p.fk_user + LEFT JOIN x_types_reglements xtr ON xtr.id = p.fk_type_reglement + WHERE p.fk_operation = ? AND p.chk_active = 1 + '; + + $params = [$operationId]; + if ($userId) { + $sql .= ' AND p.fk_user = ?'; + $params[] = $userId; + } + + $sql .= ' ORDER BY p.passed_at DESC'; + + $stmt = $this->db->prepare($sql); + $stmt->execute($params); + $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Traiter les données comme dans l'ancienne version + foreach ($passages as $p) { + // Type de passage + switch ($p["fk_type"]) { + case 1: + $ptype = "Effectué"; + $preglement = $p["reglement_libelle"]; + break; + case 2: + $ptype = "A finaliser"; + $preglement = ""; + break; + case 3: + $ptype = "Refusé"; + $preglement = ""; + break; + case 4: + $ptype = "Don"; + $preglement = ""; + break; + case 9: + $ptype = "Habitat vide"; + $preglement = ""; + break; + default: + $ptype = $p["fk_type"]; + $preglement = ""; + break; + } + + // Habitat + if ($p["fk_habitat"] == 1) { + $phabitat = "Individuel"; + } else { + $phabitat = "Etage " . $p["niveau"] . " - Appt " . $p["appt"]; + } + + // Dates + $dateEve = $p["passed_at"] ? date("d/m/Y", strtotime($p["passed_at"])) : ""; + $heureEve = $p["passed_at"] ? date("H:i", strtotime($p["passed_at"])) : ""; + + // Déchiffrer les données + $donateur = ApiService::decryptData($p["encrypted_name"]); + $email = !empty($p["encrypted_email"]) ? ApiService::decryptSearchableData($p["encrypted_email"]) : ""; + $phone = !empty($p["encrypted_phone"]) ? ApiService::decryptData($p["encrypted_phone"]) : ""; + $userName = ApiService::decryptData($p["user_name"]); + + // Nettoyer les données (comme dans l'ancienne version) + $nom = str_replace("/", "-", $userName); + $tournee = str_replace("/", "-", $p["sect_name"]); + + $aData[] = [ + $dateEve, + $heureEve, + $p["user_first_name"], + $nom, + $tournee, + $ptype, + $p["numero"] . $p["rue_bis"], + $p["rue"], + $p["ville"], + $phabitat, + $donateur, + $email, + $phone, + $p["montant"], + $preglement, + $p["remarque"] + ]; + } + + return $aData; + } + + /** + * Restaure une opération à partir d'un backup chiffré + * + * @param string $backupFilePath Chemin vers le fichier de backup + * @param int $targetEntiteId ID de l'entité cible (pour restauration cross-entité) + * @return array Résultat de la restauration + * @throws Exception En cas d'erreur de restauration + */ + public function restoreFromBackup(string $backupFilePath, int $targetEntiteId): array { + try { + // Initialiser le service de chiffrement + $backupService = new BackupEncryptionService(); + + // Lire et déchiffrer le backup + $backupData = $backupService->readBackupFile($backupFilePath); + + // Valider la structure du backup + if (!isset($backupData['operation']) || !isset($backupData['export_metadata'])) { + throw new Exception('Structure de backup invalide'); + } + + $operationData = $backupData['operation']; + $originalEntiteId = $backupData['export_metadata']['source_entite_id']; + + // Commencer la transaction + $this->db->beginTransaction(); + + // Créer la nouvelle opération + $newOperationId = $this->restoreOperation($operationData, $targetEntiteId); + + // Restaurer les utilisateurs (si même entité) + if ($targetEntiteId === $originalEntiteId && isset($backupData['users'])) { + $this->restoreUsers($backupData['users'], $newOperationId); + } + + // Restaurer les secteurs + if (isset($backupData['sectors'])) { + $this->restoreSectors($backupData['sectors'], $newOperationId); + } + + // Restaurer les relations utilisateurs-secteurs + if (isset($backupData['user_sectors'])) { + $this->restoreUserSectors($backupData['user_sectors'], $newOperationId); + } + + // Restaurer les passages + if (isset($backupData['passages'])) { + $this->restorePassages($backupData['passages'], $newOperationId); + } + + $this->db->commit(); + + LogService::log('Restauration de backup réussie', [ + 'level' => 'info', + 'backup_file' => $backupFilePath, + 'original_operation_id' => $operationData['id'], + 'new_operation_id' => $newOperationId, + 'target_entite_id' => $targetEntiteId, + 'original_entite_id' => $originalEntiteId + ]); + + return [ + 'success' => true, + 'new_operation_id' => $newOperationId, + 'original_operation_id' => $operationData['id'], + 'restored_data' => [ + 'operation' => true, + 'users' => isset($backupData['users']) && $targetEntiteId === $originalEntiteId, + 'sectors' => isset($backupData['sectors']), + 'user_sectors' => isset($backupData['user_sectors']), + 'passages' => isset($backupData['passages']) + ] + ]; + } catch (Exception $e) { + $this->db->rollBack(); + + LogService::log('Erreur lors de la restauration du backup', [ + 'level' => 'error', + 'backup_file' => $backupFilePath, + 'target_entite_id' => $targetEntiteId, + 'error' => $e->getMessage() + ]); + + throw $e; + } + } + + /** + * Restaure les données de l'opération + */ + private function restoreOperation(array $operationData, int $targetEntiteId): int { + $stmt = $this->db->prepare(' + INSERT INTO operations ( + fk_entite, libelle, date_deb, date_fin, chk_distinct_sectors, + fk_user_creat, chk_active, created_at + ) VALUES (?, ?, ?, ?, ?, ?, 0, NOW()) + '); + + $userId = Session::getUserId() ?? 1; + + $stmt->execute([ + $targetEntiteId, + $operationData['libelle'] . ' (Restaurée)', + $operationData['date_deb'], + $operationData['date_fin'], + $operationData['chk_distinct_sectors'] ?? 0, + $userId + ]); + + return (int)$this->db->lastInsertId(); + } + + /** + * Restaure les utilisateurs (uniquement si même entité) + */ + private function restoreUsers(array $users, int $newOperationId): void { + foreach ($users as $user) { + // Vérifier si l'utilisateur existe déjà + $stmt = $this->db->prepare('SELECT id FROM users WHERE id = ?'); + $stmt->execute([$user['id']]); + + if ($stmt->fetch()) { + // Associer l'utilisateur existant à la nouvelle opération + $stmt = $this->db->prepare(' + INSERT IGNORE INTO ope_users (fk_operation, fk_user, chk_active, created_at) + VALUES (?, ?, 1, NOW()) + '); + $stmt->execute([$newOperationId, $user['id']]); + } + } + } + + /** + * Restaure les secteurs + */ + private function restoreSectors(array $sectors, int $newOperationId): void { + foreach ($sectors as $sector) { + $stmt = $this->db->prepare(' + INSERT INTO ope_sectors ( + fk_operation, libelle, color, chk_active, created_at + ) VALUES (?, ?, ?, 1, NOW()) + '); + + $stmt->execute([ + $newOperationId, + $sector['libelle'], + $sector['color'] + ]); + } + } + + /** + * Restaure les relations utilisateurs-secteurs + */ + private function restoreUserSectors(array $userSectors, int $newOperationId): void { + foreach ($userSectors as $us) { + // Trouver le nouveau secteur par son libellé + $stmt = $this->db->prepare(' + SELECT id FROM ope_sectors + WHERE fk_operation = ? AND libelle = ? + LIMIT 1 + '); + $stmt->execute([$newOperationId, $us['libelle'] ?? '']); + $newSector = $stmt->fetch(); + + if ($newSector) { + $stmt = $this->db->prepare(' + INSERT IGNORE INTO ope_users_sectors ( + fk_operation, fk_sector, fk_user, chk_active, created_at + ) VALUES (?, ?, ?, 1, NOW()) + '); + + $stmt->execute([ + $newOperationId, + $newSector['id'], + $us['fk_user'] + ]); + } + } + } + + /** + * Restaure les passages + */ + private function restorePassages(array $passages, int $newOperationId): void { + foreach ($passages as $passage) { + $stmt = $this->db->prepare(' + INSERT INTO ope_pass ( + fk_operation, fk_user, fk_sector, fk_type, passed_at, + numero, rue_bis, rue, ville, fk_habitat, appt, niveau, + encrypted_name, encrypted_email, encrypted_phone, + montant, fk_type_reglement, remarque, chk_active, created_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW()) + '); + + $stmt->execute([ + $newOperationId, + $passage['fk_user'], + $passage['fk_sector'], + $passage['fk_type'], + $passage['passed_at'], + $passage['numero'], + $passage['rue_bis'], + $passage['rue'], + $passage['ville'], + $passage['fk_habitat'], + $passage['appt'], + $passage['niveau'], + $passage['encrypted_name'], + $passage['encrypted_email'], + $passage['encrypted_phone'], + $passage['montant'], + $passage['fk_type_reglement'], + $passage['remarque'] + ]); + } + } +} diff --git a/api/src/Services/FileService.php b/api/src/Services/FileService.php new file mode 100644 index 00000000..01cb3d95 --- /dev/null +++ b/api/src/Services/FileService.php @@ -0,0 +1,439 @@ +db = Database::getInstance(); + } + + /** + * Crée un dossier dans l'arborescence uploads + * + * @param int $entiteId ID de l'entité + * @param string $path Chemin relatif à partir de BASE_UPLOADS_DIR (ex: '/5/operations/2644/export') + * @return string Le chemin complet du dossier créé + */ + public function createDirectory(int $entiteId, string $path): string { + // Construire le chemin complet + $fullPath = self::BASE_UPLOADS_DIR . $path; + + LogService::log('Création de dossier', [ + 'level' => 'info', + 'entiteId' => $entiteId, + 'path' => $path, + 'fullPath' => $fullPath, + ]); + + // Créer le dossier avec tous les dossiers parents si nécessaire + if (!is_dir($fullPath)) { + if (!mkdir($fullPath, self::DIR_PERMS, true)) { + LogService::log('Erreur création dossier', [ + 'level' => 'error', + 'fullPath' => $fullPath, + ]); + throw new Exception("Impossible de créer le dossier: {$fullPath}"); + } + + // Appliquer les permissions et propriétaire + $this->setDirectoryPermissions($fullPath); + + LogService::log('Dossier créé avec succès', [ + 'level' => 'info', + 'fullPath' => $fullPath, + 'permissions' => decoct(self::DIR_PERMS), + 'owner' => self::OWNER_GROUP, + ]); + } + + return $fullPath; + } + + /** + * Applique les permissions et propriétaire sur un dossier + */ + private function setDirectoryPermissions(string $path): void { + // Appliquer les permissions + chmod($path, self::DIR_PERMS); + + // Changer le propriétaire et le groupe séparément pour plus de fiabilité + $chownUserCommand = "chown nginx " . escapeshellarg($path); + exec($chownUserCommand, $output, $returnCode); + + if ($returnCode !== 0) { + LogService::log('Avertissement: Impossible de changer le propriétaire', [ + 'level' => 'warning', + 'path' => $path, + 'command' => $chownUserCommand, + 'return_code' => $returnCode, + ]); + } + + $chgrpCommand = "chgrp nobody " . escapeshellarg($path); + exec($chgrpCommand, $output, $returnCode); + + if ($returnCode !== 0) { + LogService::log('Avertissement: Impossible de changer le groupe', [ + 'level' => 'warning', + 'path' => $path, + 'command' => $chgrpCommand, + 'return_code' => $returnCode, + ]); + } + } + + /** + * Applique les permissions sur un fichier + */ + public function setFilePermissions(string $filepath): void { + // Appliquer les permissions fichier + chmod($filepath, self::FILE_PERMS); + + // Changer le propriétaire et le groupe séparément pour plus de fiabilité + $chownUserCommand = "chown nginx " . escapeshellarg($filepath); + exec($chownUserCommand, $output, $returnCode); + + if ($returnCode !== 0) { + LogService::log('Avertissement: Impossible de changer le propriétaire du fichier', [ + 'level' => 'warning', + 'filepath' => $filepath, + 'command' => $chownUserCommand, + 'return_code' => $returnCode, + ]); + } + + $chgrpCommand = "chgrp nobody " . escapeshellarg($filepath); + exec($chgrpCommand, $output, $returnCode); + + if ($returnCode !== 0) { + LogService::log('Avertissement: Impossible de changer le groupe du fichier', [ + 'level' => 'warning', + 'filepath' => $filepath, + 'command' => $chgrpCommand, + 'return_code' => $returnCode, + ]); + } + } + + /** + * Supprime un fichier + * + * @param string $filePath Chemin complet vers le fichier ou chemin relatif depuis BASE_UPLOADS_DIR + * @param string $fileName Nom du fichier (pour les logs) + * @return bool True si suppression réussie, false sinon + */ + public function deleteFile(string $filePath, string $fileName): bool { + // Si le chemin ne commence pas par /, on considère qu'il est relatif à BASE_UPLOADS_DIR + if (!str_starts_with($filePath, '/')) { + $fullPath = self::BASE_UPLOADS_DIR . '/' . $filePath; + } else { + $fullPath = $filePath; + } + + LogService::log('Tentative de suppression de fichier', [ + 'level' => 'info', + 'fileName' => $fileName, + 'filePath' => $filePath, + 'fullPath' => $fullPath, + ]); + + // Vérifier que le fichier existe + if (!file_exists($fullPath)) { + LogService::log('Fichier non trouvé pour suppression', [ + 'level' => 'warning', + 'fileName' => $fileName, + 'fullPath' => $fullPath, + ]); + return false; + } + + // Vérifier que c'est bien un fichier (pas un dossier) + if (!is_file($fullPath)) { + LogService::log('Le chemin ne pointe pas vers un fichier', [ + 'level' => 'error', + 'fileName' => $fileName, + 'fullPath' => $fullPath, + ]); + return false; + } + + // Supprimer d'abord les enregistrements dans la table medias + $deletedMediaRecords = $this->deleteMediaRecordsByFile($fullPath, $fileName); + + // Tenter la suppression du fichier physique + if (unlink($fullPath)) { + LogService::log('Fichier supprimé avec succès', [ + 'level' => 'info', + 'fileName' => $fileName, + 'fullPath' => $fullPath, + 'deletedMediaRecords' => $deletedMediaRecords, + ]); + return true; + } else { + LogService::log('Erreur lors de la suppression du fichier', [ + 'level' => 'error', + 'fileName' => $fileName, + 'fullPath' => $fullPath, + ]); + return false; + } + } + + /** + * Supprime un dossier et tout son contenu + * + * @param string $filePath Chemin complet vers le dossier ou chemin relatif depuis BASE_UPLOADS_DIR + * @return bool True si suppression réussie, false sinon + */ + public function deleteDir(string $filePath): bool { + // Si le chemin ne commence pas par /, on considère qu'il est relatif à BASE_UPLOADS_DIR + if (!str_starts_with($filePath, '/')) { + $fullPath = self::BASE_UPLOADS_DIR . '/' . $filePath; + } else { + $fullPath = $filePath; + } + + LogService::log('Tentative de suppression de dossier', [ + 'level' => 'info', + 'filePath' => $filePath, + 'fullPath' => $fullPath, + ]); + + // Vérifier que le dossier existe + if (!file_exists($fullPath)) { + LogService::log('Dossier non trouvé pour suppression', [ + 'level' => 'warning', + 'fullPath' => $fullPath, + ]); + return false; + } + + // Vérifier que c'est bien un dossier + if (!is_dir($fullPath)) { + LogService::log('Le chemin ne pointe pas vers un dossier', [ + 'level' => 'error', + 'fullPath' => $fullPath, + ]); + return false; + } + + // Supprimer d'abord les enregistrements dans la table medias pour ce dossier + $deletedMediaRecords = $this->deleteMediaRecordsByDirectory($fullPath); + + // Supprimer récursivement le contenu du dossier + if ($this->deleteDirectoryRecursive($fullPath)) { + LogService::log('Dossier supprimé avec succès', [ + 'level' => 'info', + 'fullPath' => $fullPath, + 'deletedMediaRecords' => $deletedMediaRecords, + ]); + return true; + } else { + LogService::log('Erreur lors de la suppression du dossier', [ + 'level' => 'error', + 'fullPath' => $fullPath, + ]); + return false; + } + } + + /** + * Supprime les enregistrements medias correspondant à un fichier spécifique + * + * @param string $fullPath Chemin complet du fichier + * @param string $fileName Nom du fichier (pour les logs) + * @return int Nombre d'enregistrements supprimés + */ + private function deleteMediaRecordsByFile(string $fullPath, string $fileName): int { + // Convertir le chemin complet en chemin relatif pour la recherche en base + $relativePath = str_replace(getcwd() . '/', '', $fullPath); + + // Rechercher les enregistrements correspondants + $stmt = $this->db->prepare(' + SELECT id, fichier, file_path FROM medias + WHERE file_path = ? OR fichier = ? + '); + $stmt->execute([$relativePath, $fileName]); + $mediaRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (empty($mediaRecords)) { + LogService::log('Aucun enregistrement media trouvé pour le fichier', [ + 'level' => 'info', + 'fileName' => $fileName, + 'relativePath' => $relativePath, + ]); + return 0; + } + + // Supprimer les enregistrements + $stmt = $this->db->prepare('DELETE FROM medias WHERE file_path = ? OR fichier = ?'); + $stmt->execute([$relativePath, $fileName]); + $deletedCount = $stmt->rowCount(); + + LogService::log('Enregistrements medias supprimés pour fichier', [ + 'level' => 'info', + 'fileName' => $fileName, + 'deletedCount' => $deletedCount, + 'mediaRecords' => array_column($mediaRecords, 'id'), + ]); + + return $deletedCount; + } + + /** + * Supprime les enregistrements medias correspondant à un dossier et ses sous-dossiers + * + * @param string $fullPath Chemin complet du dossier + * @return int Nombre d'enregistrements supprimés + */ + private function deleteMediaRecordsByDirectory(string $fullPath): int { + // Convertir le chemin complet en chemin relatif pour la recherche en base + $relativePath = str_replace(getcwd() . '/', '', $fullPath); + + // Rechercher tous les enregistrements dont le chemin commence par le dossier + $stmt = $this->db->prepare(' + SELECT id, fichier, file_path FROM medias + WHERE file_path LIKE ? + '); + $stmt->execute([$relativePath . '%']); + $mediaRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (empty($mediaRecords)) { + LogService::log('Aucun enregistrement media trouvé pour le dossier', [ + 'level' => 'info', + 'relativePath' => $relativePath, + ]); + return 0; + } + + // Supprimer les enregistrements + $stmt = $this->db->prepare('DELETE FROM medias WHERE file_path LIKE ?'); + $stmt->execute([$relativePath . '%']); + $deletedCount = $stmt->rowCount(); + + LogService::log('Enregistrements medias supprimés pour dossier', [ + 'level' => 'info', + 'relativePath' => $relativePath, + 'deletedCount' => $deletedCount, + 'mediaRecords' => array_column($mediaRecords, 'id'), + ]); + + return $deletedCount; + } + + /** + * Supprime récursivement un dossier et tout son contenu + * + * @param string $dir Chemin complet vers le dossier + * @return bool True si suppression réussie, false sinon + */ + private function deleteDirectoryRecursive(string $dir): bool { + if (!is_dir($dir)) { + return false; + } + + $files = array_diff(scandir($dir), ['.', '..']); + $deletedFiles = 0; + $totalFiles = count($files); + + foreach ($files as $file) { + $filePath = $dir . DIRECTORY_SEPARATOR . $file; + + if (is_dir($filePath)) { + // Récursion pour les sous-dossiers + if ($this->deleteDirectoryRecursive($filePath)) { + $deletedFiles++; + LogService::log('Sous-dossier supprimé', [ + 'level' => 'debug', + 'subDir' => $filePath, + ]); + } else { + LogService::log('Erreur suppression sous-dossier', [ + 'level' => 'error', + 'subDir' => $filePath, + ]); + } + } else { + // Supprimer le fichier + if (unlink($filePath)) { + $deletedFiles++; + LogService::log('Fichier supprimé du dossier', [ + 'level' => 'debug', + 'file' => $filePath, + ]); + } else { + LogService::log('Erreur suppression fichier du dossier', [ + 'level' => 'error', + 'file' => $filePath, + ]); + } + } + } + + // Supprimer le dossier lui-même s'il est vide + if ($deletedFiles === $totalFiles && rmdir($dir)) { + return true; + } else { + LogService::log('Impossible de supprimer le dossier principal', [ + 'level' => 'error', + 'dir' => $dir, + 'deletedFiles' => $deletedFiles, + 'totalFiles' => $totalFiles, + ]); + return false; + } + } + + /** + * Enregistre le fichier dans la table medias + */ + public function saveToMediasTable(int $entiteId, int $operationId, string $filename, string $filepath, string $fileType, string $description, string $fileCategory = 'export'): int { + $stmt = $this->db->prepare(' + INSERT INTO medias ( + support, support_id, fichier, file_type, file_category, file_size, mime_type, + original_name, fk_entite, fk_operation, file_path, description, + created_at, fk_user_creat + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?) + '); + + // Déterminer le type MIME selon l'extension + $mimeTypes = [ + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'json' => 'application/json', + 'enc' => 'application/octet-stream' + ]; + $mimeType = $mimeTypes[$fileType] ?? 'application/octet-stream'; + + $relativePath = str_replace(getcwd() . '/', '', $filepath); + $userId = Session::getUserId() ?? 1; // Fallback si pas de session + + $stmt->execute([ + 'operation', + $operationId, + $filename, + $fileType, + $fileCategory, + filesize($filepath), + $mimeType, + $filename, + $entiteId, + $operationId, + $relativePath, + $description, + $userId + ]); + + return (int)$this->db->lastInsertId(); + } +} diff --git a/api/src/Services/OperationDataService.php b/api/src/Services/OperationDataService.php new file mode 100644 index 00000000..8b3e3d15 --- /dev/null +++ b/api/src/Services/OperationDataService.php @@ -0,0 +1,282 @@ + 2) { + // Super admin : les 3 dernières opérations + $operationLimit = 3; + } else { + // Autres cas : pas d'opérations + $operationLimit = 0; + } + + // Si une opération spécifique est demandée (création d'opération) + if ($specificOperationId) { + $operationQuery = "SELECT id, fk_entite, libelle, date_deb, date_fin, chk_active + FROM operations + WHERE fk_entite = ? + ORDER BY id DESC LIMIT 3"; + + $operationStmt = $db->prepare($operationQuery); + $operationStmt->execute([$entiteId]); + $operations = $operationStmt->fetchAll(PDO::FETCH_ASSOC); + $activeOperationId = $specificOperationId; + } elseif ($operationLimit > 0) { + $operationQuery = "SELECT id, fk_entite, libelle, date_deb, date_fin, chk_active + FROM operations + WHERE fk_entite = ?"; + + if ($activeOperationOnly) { + $operationQuery .= " AND chk_active = 1"; + } + + $operationQuery .= " ORDER BY id DESC LIMIT " . $operationLimit; + + $operationStmt = $db->prepare($operationQuery); + $operationStmt->execute([$entiteId]); + $operations = $operationStmt->fetchAll(PDO::FETCH_ASSOC); + + // Récupérer l'ID de l'opération active (première opération retournée ou celle avec chk_active=1) + $activeOperationId = null; + if (!empty($operations)) { + foreach ($operations as $operation) { + if ($operation['chk_active'] == 1) { + $activeOperationId = (int)$operation['id']; + break; + } + } + // Si aucune opération active trouvée, prendre la première + if (!$activeOperationId) { + $activeOperationId = (int)$operations[0]['id']; + } + } + } else { + $operations = []; + $activeOperationId = null; + } + + if (!empty($operations)) { + // Formater les données des opérations + foreach ($operations as $operation) { + $operationsData[] = [ + 'id' => $operation['id'], + 'fk_entite' => $operation['fk_entite'], + 'libelle' => $operation['libelle'], + 'date_deb' => $operation['date_deb'], + 'date_fin' => $operation['date_fin'], + 'chk_active' => $operation['chk_active'] + ]; + } + + // 2. Récupérer les secteurs selon l'interface et le rôle + if ($activeOperationId) { + if ($interface === 'user') { + // Interface utilisateur : seulement les secteurs affectés à l'utilisateur + $sectorsStmt = $db->prepare( + 'SELECT s.id, s.libelle, s.color, s.sector + FROM ope_sectors s + JOIN ope_users_sectors us ON s.id = us.fk_sector + WHERE us.fk_operation = ? AND us.fk_user = ? AND us.chk_active = 1 AND s.chk_active = 1' + ); + $sectorsStmt->execute([$activeOperationId, $userId]); + } elseif ($interface === 'admin' && ($userRole == 2 || $userRole > 2)) { + // Interface admin : tous les secteurs distincts de l'opération + $sectorsStmt = $db->prepare( + 'SELECT DISTINCT s.id, s.libelle, s.color, s.sector + FROM ope_sectors s + WHERE s.fk_operation = ? AND s.chk_active = 1' + ); + $sectorsStmt->execute([$activeOperationId]); + } else { + $sectors = []; + } + + // Récupération des secteurs si une requête a été préparée + if (isset($sectorsStmt)) { + $sectors = $sectorsStmt->fetchAll(PDO::FETCH_ASSOC); + } else { + $sectors = []; + } + + if (!empty($sectors)) { + $sectorsData = $sectors; + + // 3. Récupérer les passages selon l'interface et le rôle + if ($interface === 'user' && !empty($sectors)) { + // Interface utilisateur : passages liés aux secteurs de l'utilisateur + $sectorIds = array_column($sectors, 'id'); + $sectorIdsString = implode(',', $sectorIds); + + if (!empty($sectorIdsString)) { + $passagesStmt = $db->prepare( + "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau, + gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages, + chk_email_sent, docremis, date_repasser, chk_mobile, anomalie, created_at, updated_at, chk_active + FROM ope_pass + WHERE fk_operation = ? AND fk_sector IN ($sectorIdsString) AND chk_active = 1" + ); + $passagesStmt->execute([$activeOperationId]); + } + } elseif ($interface === 'admin' && ($userRole == 2 || $userRole > 2)) { + // Interface admin : tous les passages de l'opération + $passagesStmt = $db->prepare( + "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau, + gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages, + chk_email_sent, docremis, date_repasser, chk_mobile, anomalie, created_at, updated_at, chk_active + FROM ope_pass + WHERE fk_operation = ? AND chk_active = 1" + ); + $passagesStmt->execute([$activeOperationId]); + } else { + $passages = []; + } + + // Récupération des passages si une requête a été préparée + if (isset($passagesStmt)) { + $passages = $passagesStmt->fetchAll(PDO::FETCH_ASSOC); + } else { + $passages = []; + } + + if (!empty($passages)) { + // Déchiffrer les données sensibles + foreach ($passages as &$passage) { + // Déchiffrement du nom + $passage['name'] = ''; + if (!empty($passage['encrypted_name'])) { + $passage['name'] = ApiService::decryptData($passage['encrypted_name']); + } + unset($passage['encrypted_name']); + + // Déchiffrement de l'email + $passage['email'] = ''; + if (!empty($passage['encrypted_email'])) { + $decryptedEmail = ApiService::decryptSearchableData($passage['encrypted_email']); + if ($decryptedEmail) { + $passage['email'] = $decryptedEmail; + } + } + unset($passage['encrypted_email']); + + // Déchiffrement du téléphone + $passage['phone'] = ''; + if (!empty($passage['encrypted_phone'])) { + $passage['phone'] = ApiService::decryptData($passage['encrypted_phone']); + } + unset($passage['encrypted_phone']); + } + $passagesData = $passages; + } + + // 4. Récupérer les utilisateurs des secteurs partagés + if (($interface === 'user' || ($interface === 'admin' && ($userRole == 2 || $userRole > 2))) && !empty($sectors)) { + $sectorIds = array_column($sectors, 'id'); + $sectorIdsString = implode(',', $sectorIds); + + if (!empty($sectorIdsString)) { + // Utiliser ope_users au lieu de users pour avoir les données historiques + $usersSectorsStmt = $db->prepare( + "SELECT DISTINCT ou.fk_user as id, ou.first_name, ou.encrypted_name, ou.sect_name, us.fk_sector + FROM ope_users ou + JOIN ope_users_sectors us ON ou.fk_user = us.fk_user AND ou.fk_operation = us.fk_operation + WHERE us.fk_sector IN ($sectorIdsString) + AND us.fk_operation = ? + AND us.chk_active = 1 + AND ou.chk_active = 1 + AND ou.fk_user != ?" // Exclure l'utilisateur connecté + ); + $usersSectorsStmt->execute([$activeOperationId, $userId]); + $usersSectors = $usersSectorsStmt->fetchAll(PDO::FETCH_ASSOC); + + if (!empty($usersSectors)) { + // Déchiffrer les noms des utilisateurs + foreach ($usersSectors as &$userSector) { + if (!empty($userSector['encrypted_name'])) { + $userSector['name'] = ApiService::decryptData($userSector['encrypted_name']); + unset($userSector['encrypted_name']); + } + } + $usersSectorsData = $usersSectors; + } + } + } + } + } + } + + return [ + 'operations' => $operationsData, + 'sectors' => $sectorsData, + 'users_sectors' => $usersSectorsData, + 'passages' => $passagesData + ]; + } + + /** + * Prépare la réponse complète pour la création d'opération + * + * @param PDO $db Instance de la base de données + * @param int $newOpeId ID de la nouvelle opération + * @param int $entiteId ID de l'entité + * @return array Réponse formatée avec status, message, operation_id et données + */ + public static function prepareOperationResponse(PDO $db, int $newOpeId, int $entiteId): array { + // Utiliser le rôle admin pour récupérer toutes les données + $operationData = self::prepareOperationData($db, $entiteId, 'admin', 2, 0, $newOpeId); + + return [ + 'status' => 'success', + 'message' => 'Opération créée avec succès', + 'operation_id' => $newOpeId, + 'operations' => $operationData['operations'], + 'sectors' => $operationData['sectors'], + 'users_sectors' => $operationData['users_sectors'], + 'passages' => $operationData['passages'] + ]; + } +} diff --git a/api/vendor/autoload.php b/api/vendor/autoload.php index 8ddac8fe..6d38b9de 100644 --- a/api/vendor/autoload.php +++ b/api/vendor/autoload.php @@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/api/vendor/composer/InstalledVersions.php b/api/vendor/composer/InstalledVersions.php index 6d29bff6..2052022f 100644 --- a/api/vendor/composer/InstalledVersions.php +++ b/api/vendor/composer/InstalledVersions.php @@ -26,6 +26,12 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null @@ -322,6 +328,18 @@ class InstalledVersions self::$installedIsLocalDir = false; } + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + /** * @return array[] * @psalm-return list}> @@ -336,7 +354,7 @@ class InstalledVersions $copiedLocalDir = false; if (self::$canGetVendors) { - $selfDir = strtr(__DIR__, '\\', '/'); + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { diff --git a/api/vendor/composer/autoload_classmap.php b/api/vendor/composer/autoload_classmap.php index c20440c1..1dcee676 100644 --- a/api/vendor/composer/autoload_classmap.php +++ b/api/vendor/composer/autoload_classmap.php @@ -6,7 +6,59 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'ApiService' => $baseDir . '/src/Services/ApiService.php', + 'AppConfig' => $baseDir . '/src/Config/AppConfig.php', + 'App\\Controllers\\EntiteController' => $baseDir . '/src/Controllers/EntiteController.php', + 'App\\Controllers\\FileController' => $baseDir . '/src/Controllers/FileController.php', + 'App\\Controllers\\LoginController' => $baseDir . '/src/Controllers/LoginController.php', + 'App\\Controllers\\OperationController' => $baseDir . '/src/Controllers/OperationController.php', + 'App\\Controllers\\PassageController' => $baseDir . '/src/Controllers/PassageController.php', + 'App\\Controllers\\UserController' => $baseDir . '/src/Controllers/UserController.php', + 'App\\Controllers\\VilleController' => $baseDir . '/src/Controllers/VilleController.php', + 'BackupEncryptionService' => $baseDir . '/src/Services/BackupEncryptionService.php', + 'ClientDetector' => $baseDir . '/src/Utils/ClientDetector.php', + 'Complex\\Complex' => $vendorDir . '/markbaker/complex/classes/src/Complex.php', + 'Complex\\Exception' => $vendorDir . '/markbaker/complex/classes/src/Exception.php', + 'Complex\\Functions' => $vendorDir . '/markbaker/complex/classes/src/Functions.php', + 'Complex\\Operations' => $vendorDir . '/markbaker/complex/classes/src/Operations.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'Composer\\Pcre\\MatchAllResult' => $vendorDir . '/composer/pcre/src/MatchAllResult.php', + 'Composer\\Pcre\\MatchAllStrictGroupsResult' => $vendorDir . '/composer/pcre/src/MatchAllStrictGroupsResult.php', + 'Composer\\Pcre\\MatchAllWithOffsetsResult' => $vendorDir . '/composer/pcre/src/MatchAllWithOffsetsResult.php', + 'Composer\\Pcre\\MatchResult' => $vendorDir . '/composer/pcre/src/MatchResult.php', + 'Composer\\Pcre\\MatchStrictGroupsResult' => $vendorDir . '/composer/pcre/src/MatchStrictGroupsResult.php', + 'Composer\\Pcre\\MatchWithOffsetsResult' => $vendorDir . '/composer/pcre/src/MatchWithOffsetsResult.php', + 'Composer\\Pcre\\PHPStan\\InvalidRegexPatternRule' => $vendorDir . '/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php', + 'Composer\\Pcre\\PHPStan\\PregMatchFlags' => $vendorDir . '/composer/pcre/src/PHPStan/PregMatchFlags.php', + 'Composer\\Pcre\\PHPStan\\PregMatchParameterOutTypeExtension' => $vendorDir . '/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php', + 'Composer\\Pcre\\PHPStan\\PregMatchTypeSpecifyingExtension' => $vendorDir . '/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php', + 'Composer\\Pcre\\PHPStan\\PregReplaceCallbackClosureTypeExtension' => $vendorDir . '/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php', + 'Composer\\Pcre\\PHPStan\\UnsafeStrictGroupsCallRule' => $vendorDir . '/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php', + 'Composer\\Pcre\\PcreException' => $vendorDir . '/composer/pcre/src/PcreException.php', + 'Composer\\Pcre\\Preg' => $vendorDir . '/composer/pcre/src/Preg.php', + 'Composer\\Pcre\\Regex' => $vendorDir . '/composer/pcre/src/Regex.php', + 'Composer\\Pcre\\ReplaceResult' => $vendorDir . '/composer/pcre/src/ReplaceResult.php', + 'Composer\\Pcre\\UnexpectedNullMatchException' => $vendorDir . '/composer/pcre/src/UnexpectedNullMatchException.php', + 'Database' => $baseDir . '/src/Core/Database.php', + 'EmailTemplates' => $baseDir . '/src/Services/EmailTemplates.php', + 'ExportService' => $baseDir . '/src/Services/ExportService.php', + 'LogController' => $baseDir . '/src/Controllers/LogController.php', + 'LogService' => $baseDir . '/src/Services/LogService.php', + 'Matrix\\Builder' => $vendorDir . '/markbaker/matrix/classes/src/Builder.php', + 'Matrix\\Decomposition\\Decomposition' => $vendorDir . '/markbaker/matrix/classes/src/Decomposition/Decomposition.php', + 'Matrix\\Decomposition\\LU' => $vendorDir . '/markbaker/matrix/classes/src/Decomposition/LU.php', + 'Matrix\\Decomposition\\QR' => $vendorDir . '/markbaker/matrix/classes/src/Decomposition/QR.php', + 'Matrix\\Div0Exception' => $vendorDir . '/markbaker/matrix/classes/src/Div0Exception.php', + 'Matrix\\Exception' => $vendorDir . '/markbaker/matrix/classes/src/Exception.php', + 'Matrix\\Functions' => $vendorDir . '/markbaker/matrix/classes/src/Functions.php', + 'Matrix\\Matrix' => $vendorDir . '/markbaker/matrix/classes/src/Matrix.php', + 'Matrix\\Operations' => $vendorDir . '/markbaker/matrix/classes/src/Operations.php', + 'Matrix\\Operators\\Addition' => $vendorDir . '/markbaker/matrix/classes/src/Operators/Addition.php', + 'Matrix\\Operators\\DirectSum' => $vendorDir . '/markbaker/matrix/classes/src/Operators/DirectSum.php', + 'Matrix\\Operators\\Division' => $vendorDir . '/markbaker/matrix/classes/src/Operators/Division.php', + 'Matrix\\Operators\\Multiplication' => $vendorDir . '/markbaker/matrix/classes/src/Operators/Multiplication.php', + 'Matrix\\Operators\\Operator' => $vendorDir . '/markbaker/matrix/classes/src/Operators/Operator.php', + 'Matrix\\Operators\\Subtraction' => $vendorDir . '/markbaker/matrix/classes/src/Operators/Subtraction.php', 'PHPMailer\\PHPMailer\\DSNConfigurator' => $vendorDir . '/phpmailer/phpmailer/src/DSNConfigurator.php', 'PHPMailer\\PHPMailer\\Exception' => $vendorDir . '/phpmailer/phpmailer/src/Exception.php', 'PHPMailer\\PHPMailer\\OAuth' => $vendorDir . '/phpmailer/phpmailer/src/OAuth.php', @@ -14,4 +66,554 @@ return array( 'PHPMailer\\PHPMailer\\PHPMailer' => $vendorDir . '/phpmailer/phpmailer/src/PHPMailer.php', 'PHPMailer\\PHPMailer\\POP3' => $vendorDir . '/phpmailer/phpmailer/src/POP3.php', 'PHPMailer\\PHPMailer\\SMTP' => $vendorDir . '/phpmailer/phpmailer/src/SMTP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\ArrayEnabled' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Category' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DAverage' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DCount' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DCountA' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DGet' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DMax' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DMin' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DProduct' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DStDev' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DStDevP' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DSum' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DVar' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DVarP' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DatabaseAbstract' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Constants' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Current' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Date' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateParts' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Days' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Days360' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Difference' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Helpers' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Month' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\NetworkDays' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Time' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\TimeParts' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\TimeValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Week' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\WorkDay' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\YearFrac' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\ArrayArgumentHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\ArrayArgumentProcessor' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\BranchPruner' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\CyclicReferenceStack' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\FormattedNumber' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Logger' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Operands\\Operand' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Operands\\StructuredReference' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselI' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselJ' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselK' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselY' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BitWise' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Compare' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Complex' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ComplexFunctions' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ComplexOperations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Constants' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertBinary' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertDecimal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertHex' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertOctal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertUOM' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\EngineeringValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Erf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ErfC' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Exception' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\ExceptionHandler' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Amortization' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\CashFlowValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Cumulative' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Interest' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\InterestAndPrincipal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Payments' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Single' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\Periodic' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Constants' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Coupons' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Depreciation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Dollar' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\FinancialValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\InterestRate' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\AccruedInterest' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Price' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Rates' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\SecurityValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Yields' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\TreasuryBill' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\FormulaParser' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\FormulaToken' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Functions' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\ErrorValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\ExcelError' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\Value' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Internal\\MakeMatrix' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Internal\\WildcardMatch' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Boolean' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Conditional' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Operations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\ExcelMatch' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Filter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Formula' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\HLookup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Helpers' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Hyperlink' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Indirect' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Lookup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupRefValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Matrix' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Offset' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\RowColumnInformation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Selection' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Sort' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Unique' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\VLookup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Absolute' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Angle' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Arabic' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Base' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Ceiling' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Combinations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Exp' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Factorial' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Floor' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Gcd' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Helpers' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\IntClass' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Lcm' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Logarithms' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\MatrixFunctions' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Operations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Random' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Roman' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Round' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\SeriesSum' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sign' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sqrt' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Subtotal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sum' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\SumSquares' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cosecant' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cosine' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cotangent' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Secant' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Sine' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Tangent' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trunc' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\AggregateBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Averages' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Averages\\Mean' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Conditional' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Confidence' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Counts' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Deviations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Beta' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Binomial' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\ChiSquared' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\DistributionValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Exponential' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\F' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Fisher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Gamma' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\GammaBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\HyperGeometric' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\LogNormal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\NewtonRaphson' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Normal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Poisson' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\StandardNormal' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\StudentT' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Weibull' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\MaxMinBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Maximum' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Minimum' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Percentiles' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Permutations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Size' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\StandardDeviations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Standardize' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\StatisticalValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Trends' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\VarianceBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Variances' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\CaseConvert' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\CharacterConvert' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Concatenate' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Extract' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Format' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Helpers' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Replace' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Search' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Text' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Trim' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Token\\Stack' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Web\\Service' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php', + 'PhpOffice\\PhpSpreadsheet\\CellReferenceHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AddressHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AddressRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AdvancedValueBinder' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Cell' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\CellAddress' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\CellRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\ColumnRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataType' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataValidation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataValidator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DefaultValueBinder' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Hyperlink' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\IValueBinder' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\IgnoredErrors' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IgnoredErrors.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\RowRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\StringValueBinder' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Axis' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\AxisText' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Chart' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\ChartColor' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\DataSeries' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\DataSeriesValues' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Exception' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\GridLines' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Layout' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Legend' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\PlotArea' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\IRenderer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\JpGraph' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\JpGraphRendererBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\MtJpGraphRenderer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Title' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\TrendLine' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Cells' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\CellsFactory' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Memory\\SimpleCache1' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Memory\\SimpleCache3' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php', + 'PhpOffice\\PhpSpreadsheet\\Comment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php', + 'PhpOffice\\PhpSpreadsheet\\DefinedName' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php', + 'PhpOffice\\PhpSpreadsheet\\Document\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Document\\Security' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php', + 'PhpOffice\\PhpSpreadsheet\\Exception' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\HashTable' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Dimension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Downloader' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Handler' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Html' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Sample' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Size' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\TextGrid' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php', + 'PhpOffice\\PhpSpreadsheet\\IComparable' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php', + 'PhpOffice\\PhpSpreadsheet\\IOFactory' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php', + 'PhpOffice\\PhpSpreadsheet\\NamedFormula' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php', + 'PhpOffice\\PhpSpreadsheet\\NamedRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\BaseReader' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Csv' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Csv\\Delimiter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\DefaultReadFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Exception' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\PageSetup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\Styles' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Html' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\IReadFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\IReader' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\AutoFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\BaseLoader' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\DefinedNames' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\FormulaTranslator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\PageSettings' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Security\\XmlScanner' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Slk' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BIFF5' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BIFF8' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BuiltIn' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\ConditionalFormatting' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\DataValidationHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\ErrorCode' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Escher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\MD5' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\RC4' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\Border' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\CellAlignment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\CellFont' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\FillPattern' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\AutoFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\BaseParserClass' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Chart' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\ColumnAndRowAttributes' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\ConditionalStyles' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\DataValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Hyperlinks' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Namespaces' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\PageSetup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SharedFormula' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SheetViewOptions' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SheetViews' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Styles' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\TableReader' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Theme' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\WorkbookView' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\DataValidations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\PageSettings' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Properties' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Alignment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Border' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Fill' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Font' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\NumberFormat' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\StyleBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php', + 'PhpOffice\\PhpSpreadsheet\\ReferenceHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\ITextElement' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\RichText' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\Run' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\TextElement' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php', + 'PhpOffice\\PhpSpreadsheet\\Settings' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\CodePage' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Date' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Drawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer\\SpgrContainer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer\\SpgrContainer\\SpContainer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer\\BSE' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer\\BSE\\Blip' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\File' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Font' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\IntOrFloat' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLERead' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\ChainedBlockStream' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS\\File' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS\\Root' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\PasswordHasher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\StringHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\TimeZone' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\BestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\ExponentialBestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\LinearBestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\LogarithmicBestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\PolynomialBestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\PowerBestFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\Trend' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\XMLWriter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Xls' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Spreadsheet' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Alignment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Border' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Borders' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Color' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Conditional' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\CellMatcher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\CellStyleAssessor' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalColorScale' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalDataBar' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalDataBarExtension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalFormatValueObject' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalFormattingRuleExtension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\StyleMerger' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Blanks' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\CellValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\DateValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Duplicates' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Errors' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Expression' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\TextValue' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\WizardAbstract' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\WizardInterface' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Fill' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Font' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\BaseFormatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\DateFormatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Formatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\FractionFormatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\NumberFormatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\PercentageFormatter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Accounting' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Currency' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Date' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\DateTime' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\DateTimeWizard' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Duration' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Locale' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Number' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Number.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\NumberBase' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Percentage' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Percentage.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Scientific' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Scientific.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Time' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Time.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Wizard' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Wizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Protection' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\RgbTint' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/RgbTint.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Style' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Supervisor' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php', + 'PhpOffice\\PhpSpreadsheet\\Theme' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter\\Column' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter\\Column\\Rule' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFit' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFit.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\BaseDrawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\CellIterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Column' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnCellIterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnDimension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnIterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Dimension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Drawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Drawing\\Shadow' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\HeaderFooter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\HeaderFooterDrawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Iterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\MemoryDrawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageBreak' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageBreak.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageMargins' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageSetup' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Pane' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Pane.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ProtectedRange' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ProtectedRange.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Protection' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Row' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowCellIterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowDimension' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowIterator' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\SheetView' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table\\Column' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table\\TableStyle' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Validations' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Worksheet' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\BaseWriter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Csv' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Exception' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Html' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\IWriter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\AutoFilters' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Cell\\Comment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Cell\\Style' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Content' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Formula' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Meta' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\MetaInf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Mimetype' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\NamedExpressions' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Settings' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Styles' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Thumbnails' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\WriterPart' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Dompdf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Mpdf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Tcpdf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\BIFFwriter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\CellDataValidation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\ConditionalHelper' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\ErrorCode' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Escher' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Font' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Parser' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellAlignment' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellBorder' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellFill' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\ColorMap' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Workbook' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Worksheet' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Xf' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\AutoFilter' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Chart' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Comments' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\ContentTypes' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\DefinedNames' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\DocProps' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Drawing' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\FunctionPrefix' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Rels' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\RelsRibbon' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\RelsVBA' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\StringTable' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Style' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Table' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Theme' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Workbook' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Worksheet' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\WriterPart' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream0' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream0.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream2' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream2.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream3' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream3.php', + 'Psr\\Http\\Client\\ClientExceptionInterface' => $vendorDir . '/psr/http-client/src/ClientExceptionInterface.php', + 'Psr\\Http\\Client\\ClientInterface' => $vendorDir . '/psr/http-client/src/ClientInterface.php', + 'Psr\\Http\\Client\\NetworkExceptionInterface' => $vendorDir . '/psr/http-client/src/NetworkExceptionInterface.php', + 'Psr\\Http\\Client\\RequestExceptionInterface' => $vendorDir . '/psr/http-client/src/RequestExceptionInterface.php', + 'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php', + 'Psr\\Http\\Message\\RequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/RequestFactoryInterface.php', + 'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php', + 'Psr\\Http\\Message\\ResponseFactoryInterface' => $vendorDir . '/psr/http-factory/src/ResponseFactoryInterface.php', + 'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php', + 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/ServerRequestFactoryInterface.php', + 'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php', + 'Psr\\Http\\Message\\StreamFactoryInterface' => $vendorDir . '/psr/http-factory/src/StreamFactoryInterface.php', + 'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php', + 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => $vendorDir . '/psr/http-factory/src/UploadedFileFactoryInterface.php', + 'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php', + 'Psr\\Http\\Message\\UriFactoryInterface' => $vendorDir . '/psr/http-factory/src/UriFactoryInterface.php', + 'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php', + 'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php', + 'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php', + 'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php', + 'Request' => $baseDir . '/src/Core/Request.php', + 'Response' => $baseDir . '/src/Core/Response.php', + 'Router' => $baseDir . '/src/Core/Router.php', + 'Session' => $baseDir . '/src/Core/Session.php', + 'ZipStream\\CentralDirectoryFileHeader' => $vendorDir . '/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php', + 'ZipStream\\CompressionMethod' => $vendorDir . '/maennchen/zipstream-php/src/CompressionMethod.php', + 'ZipStream\\DataDescriptor' => $vendorDir . '/maennchen/zipstream-php/src/DataDescriptor.php', + 'ZipStream\\EndOfCentralDirectory' => $vendorDir . '/maennchen/zipstream-php/src/EndOfCentralDirectory.php', + 'ZipStream\\Exception' => $vendorDir . '/maennchen/zipstream-php/src/Exception.php', + 'ZipStream\\Exception\\DosTimeOverflowException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php', + 'ZipStream\\Exception\\FileNotFoundException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/FileNotFoundException.php', + 'ZipStream\\Exception\\FileNotReadableException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/FileNotReadableException.php', + 'ZipStream\\Exception\\FileSizeIncorrectException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php', + 'ZipStream\\Exception\\OverflowException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/OverflowException.php', + 'ZipStream\\Exception\\ResourceActionException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/ResourceActionException.php', + 'ZipStream\\Exception\\SimulationFileUnknownException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php', + 'ZipStream\\Exception\\StreamNotReadableException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php', + 'ZipStream\\Exception\\StreamNotSeekableException' => $vendorDir . '/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php', + 'ZipStream\\File' => $vendorDir . '/maennchen/zipstream-php/src/File.php', + 'ZipStream\\GeneralPurposeBitFlag' => $vendorDir . '/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php', + 'ZipStream\\LocalFileHeader' => $vendorDir . '/maennchen/zipstream-php/src/LocalFileHeader.php', + 'ZipStream\\OperationMode' => $vendorDir . '/maennchen/zipstream-php/src/OperationMode.php', + 'ZipStream\\PackField' => $vendorDir . '/maennchen/zipstream-php/src/PackField.php', + 'ZipStream\\Time' => $vendorDir . '/maennchen/zipstream-php/src/Time.php', + 'ZipStream\\Version' => $vendorDir . '/maennchen/zipstream-php/src/Version.php', + 'ZipStream\\Zip64\\DataDescriptor' => $vendorDir . '/maennchen/zipstream-php/src/Zip64/DataDescriptor.php', + 'ZipStream\\Zip64\\EndOfCentralDirectory' => $vendorDir . '/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php', + 'ZipStream\\Zip64\\EndOfCentralDirectoryLocator' => $vendorDir . '/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php', + 'ZipStream\\Zip64\\ExtendedInformationExtraField' => $vendorDir . '/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php', + 'ZipStream\\ZipStream' => $vendorDir . '/maennchen/zipstream-php/src/ZipStream.php', + 'ZipStream\\Zs\\ExtendedInformationExtraField' => $vendorDir . '/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php', ); diff --git a/api/vendor/composer/autoload_psr4.php b/api/vendor/composer/autoload_psr4.php index 452e39f8..f5a70e61 100644 --- a/api/vendor/composer/autoload_psr4.php +++ b/api/vendor/composer/autoload_psr4.php @@ -6,6 +6,13 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), + 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), + 'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), - 'App\\' => array($baseDir . '/src'), + 'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'), + 'Composer\\Pcre\\' => array($vendorDir . '/composer/pcre/src'), + 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'), ); diff --git a/api/vendor/composer/autoload_real.php b/api/vendor/composer/autoload_real.php index d2aba9b2..868e577d 100644 --- a/api/vendor/composer/autoload_real.php +++ b/api/vendor/composer/autoload_real.php @@ -22,8 +22,6 @@ class ComposerAutoloaderInit03e608fa83a14a82b3f9223977e9674e return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInit03e608fa83a14a82b3f9223977e9674e', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInit03e608fa83a14a82b3f9223977e9674e', 'loadClassLoader')); diff --git a/api/vendor/composer/autoload_static.php b/api/vendor/composer/autoload_static.php index 620bbd1f..ab104bd8 100644 --- a/api/vendor/composer/autoload_static.php +++ b/api/vendor/composer/autoload_static.php @@ -7,29 +7,123 @@ namespace Composer\Autoload; class ComposerStaticInit03e608fa83a14a82b3f9223977e9674e { public static $prefixLengthsPsr4 = array ( + 'Z' => + array ( + 'ZipStream\\' => 10, + ), 'P' => array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Http\\Message\\' => 17, + 'Psr\\Http\\Client\\' => 16, + 'PhpOffice\\PhpSpreadsheet\\' => 25, 'PHPMailer\\PHPMailer\\' => 20, ), - 'A' => + 'M' => array ( - 'App\\' => 4, + 'Matrix\\' => 7, + ), + 'C' => + array ( + 'Composer\\Pcre\\' => 14, + 'Complex\\' => 8, ), ); public static $prefixDirsPsr4 = array ( + 'ZipStream\\' => + array ( + 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Http\\Message\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', + ), + 'Psr\\Http\\Client\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-client/src', + ), + 'PhpOffice\\PhpSpreadsheet\\' => + array ( + 0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet', + ), 'PHPMailer\\PHPMailer\\' => array ( 0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src', ), - 'App\\' => + 'Matrix\\' => array ( - 0 => __DIR__ . '/../..' . '/src', + 0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src', + ), + 'Composer\\Pcre\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/pcre/src', + ), + 'Complex\\' => + array ( + 0 => __DIR__ . '/..' . '/markbaker/complex/classes/src', ), ); public static $classMap = array ( + 'ApiService' => __DIR__ . '/../..' . '/src/Services/ApiService.php', + 'AppConfig' => __DIR__ . '/../..' . '/src/Config/AppConfig.php', + 'App\\Controllers\\EntiteController' => __DIR__ . '/../..' . '/src/Controllers/EntiteController.php', + 'App\\Controllers\\FileController' => __DIR__ . '/../..' . '/src/Controllers/FileController.php', + 'App\\Controllers\\LoginController' => __DIR__ . '/../..' . '/src/Controllers/LoginController.php', + 'App\\Controllers\\OperationController' => __DIR__ . '/../..' . '/src/Controllers/OperationController.php', + 'App\\Controllers\\PassageController' => __DIR__ . '/../..' . '/src/Controllers/PassageController.php', + 'App\\Controllers\\UserController' => __DIR__ . '/../..' . '/src/Controllers/UserController.php', + 'App\\Controllers\\VilleController' => __DIR__ . '/../..' . '/src/Controllers/VilleController.php', + 'BackupEncryptionService' => __DIR__ . '/../..' . '/src/Services/BackupEncryptionService.php', + 'ClientDetector' => __DIR__ . '/../..' . '/src/Utils/ClientDetector.php', + 'Complex\\Complex' => __DIR__ . '/..' . '/markbaker/complex/classes/src/Complex.php', + 'Complex\\Exception' => __DIR__ . '/..' . '/markbaker/complex/classes/src/Exception.php', + 'Complex\\Functions' => __DIR__ . '/..' . '/markbaker/complex/classes/src/Functions.php', + 'Complex\\Operations' => __DIR__ . '/..' . '/markbaker/complex/classes/src/Operations.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Composer\\Pcre\\MatchAllResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchAllResult.php', + 'Composer\\Pcre\\MatchAllStrictGroupsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchAllStrictGroupsResult.php', + 'Composer\\Pcre\\MatchAllWithOffsetsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchAllWithOffsetsResult.php', + 'Composer\\Pcre\\MatchResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchResult.php', + 'Composer\\Pcre\\MatchStrictGroupsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchStrictGroupsResult.php', + 'Composer\\Pcre\\MatchWithOffsetsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchWithOffsetsResult.php', + 'Composer\\Pcre\\PHPStan\\InvalidRegexPatternRule' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php', + 'Composer\\Pcre\\PHPStan\\PregMatchFlags' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/PregMatchFlags.php', + 'Composer\\Pcre\\PHPStan\\PregMatchParameterOutTypeExtension' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php', + 'Composer\\Pcre\\PHPStan\\PregMatchTypeSpecifyingExtension' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php', + 'Composer\\Pcre\\PHPStan\\PregReplaceCallbackClosureTypeExtension' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php', + 'Composer\\Pcre\\PHPStan\\UnsafeStrictGroupsCallRule' => __DIR__ . '/..' . '/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php', + 'Composer\\Pcre\\PcreException' => __DIR__ . '/..' . '/composer/pcre/src/PcreException.php', + 'Composer\\Pcre\\Preg' => __DIR__ . '/..' . '/composer/pcre/src/Preg.php', + 'Composer\\Pcre\\Regex' => __DIR__ . '/..' . '/composer/pcre/src/Regex.php', + 'Composer\\Pcre\\ReplaceResult' => __DIR__ . '/..' . '/composer/pcre/src/ReplaceResult.php', + 'Composer\\Pcre\\UnexpectedNullMatchException' => __DIR__ . '/..' . '/composer/pcre/src/UnexpectedNullMatchException.php', + 'Database' => __DIR__ . '/../..' . '/src/Core/Database.php', + 'EmailTemplates' => __DIR__ . '/../..' . '/src/Services/EmailTemplates.php', + 'ExportService' => __DIR__ . '/../..' . '/src/Services/ExportService.php', + 'LogController' => __DIR__ . '/../..' . '/src/Controllers/LogController.php', + 'LogService' => __DIR__ . '/../..' . '/src/Services/LogService.php', + 'Matrix\\Builder' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Builder.php', + 'Matrix\\Decomposition\\Decomposition' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Decomposition/Decomposition.php', + 'Matrix\\Decomposition\\LU' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Decomposition/LU.php', + 'Matrix\\Decomposition\\QR' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Decomposition/QR.php', + 'Matrix\\Div0Exception' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Div0Exception.php', + 'Matrix\\Exception' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Exception.php', + 'Matrix\\Functions' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions.php', + 'Matrix\\Matrix' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Matrix.php', + 'Matrix\\Operations' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations.php', + 'Matrix\\Operators\\Addition' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/Addition.php', + 'Matrix\\Operators\\DirectSum' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/DirectSum.php', + 'Matrix\\Operators\\Division' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/Division.php', + 'Matrix\\Operators\\Multiplication' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/Multiplication.php', + 'Matrix\\Operators\\Operator' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/Operator.php', + 'Matrix\\Operators\\Subtraction' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operators/Subtraction.php', 'PHPMailer\\PHPMailer\\DSNConfigurator' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/DSNConfigurator.php', 'PHPMailer\\PHPMailer\\Exception' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/Exception.php', 'PHPMailer\\PHPMailer\\OAuth' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/OAuth.php', @@ -37,6 +131,556 @@ class ComposerStaticInit03e608fa83a14a82b3f9223977e9674e 'PHPMailer\\PHPMailer\\PHPMailer' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/PHPMailer.php', 'PHPMailer\\PHPMailer\\POP3' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/POP3.php', 'PHPMailer\\PHPMailer\\SMTP' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/SMTP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\ArrayEnabled' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Category' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DAverage' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DCount' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DCountA' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DGet' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DMax' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DMin' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DProduct' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DStDev' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DStDevP' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DSum' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DVar' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DVarP' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DatabaseAbstract' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Constants' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Current' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Date' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateParts' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Days' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Days360' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Difference' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Helpers' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Month' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\NetworkDays' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Time' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\TimeParts' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\TimeValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Week' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\WorkDay' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\YearFrac' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\ArrayArgumentHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\ArrayArgumentProcessor' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\BranchPruner' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\CyclicReferenceStack' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\FormattedNumber' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Logger' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Operands\\Operand' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\Operands\\StructuredReference' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselI' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselJ' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselK' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BesselY' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\BitWise' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Compare' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Complex' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ComplexFunctions' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ComplexOperations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Constants' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertBinary' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertDecimal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertHex' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertOctal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ConvertUOM' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\EngineeringValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\Erf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering\\ErfC' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Exception' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\ExceptionHandler' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Amortization' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\CashFlowValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Cumulative' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Interest' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\InterestAndPrincipal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Constant\\Periodic\\Payments' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Single' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\Periodic' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Constants' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Coupons' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Depreciation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Dollar' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\FinancialValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\InterestRate' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\AccruedInterest' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Price' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Rates' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\SecurityValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Yields' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\TreasuryBill' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\FormulaParser' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\FormulaToken' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Functions' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\ErrorValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\ExcelError' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\Value' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Internal\\MakeMatrix' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Internal\\WildcardMatch' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Boolean' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Conditional' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Logical\\Operations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\ExcelMatch' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Filter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Formula' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\HLookup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Helpers' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Hyperlink' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Indirect' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Lookup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupRefValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Matrix' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Offset' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\RowColumnInformation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Selection' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Sort' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Unique' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\VLookup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Absolute' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Angle' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Arabic' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Base' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Ceiling' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Combinations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Exp' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Factorial' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Floor' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Gcd' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Helpers' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\IntClass' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Lcm' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Logarithms' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\MatrixFunctions' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Operations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Random' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Roman' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Round' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\SeriesSum' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sign' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sqrt' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Subtotal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Sum' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\SumSquares' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cosecant' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cosine' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Cotangent' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Secant' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Sine' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trig\\Tangent' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Trunc' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\AggregateBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Averages' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Averages\\Mean' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Conditional' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Confidence' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Counts' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Deviations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Beta' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Binomial' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\ChiSquared' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\DistributionValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Exponential' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\F' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Fisher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Gamma' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\GammaBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\HyperGeometric' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\LogNormal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\NewtonRaphson' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Normal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Poisson' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\StandardNormal' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\StudentT' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\Weibull' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\MaxMinBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Maximum' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Minimum' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Percentiles' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Permutations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Size' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\StandardDeviations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Standardize' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\StatisticalValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Trends' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\VarianceBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Variances' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\CaseConvert' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\CharacterConvert' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Concatenate' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Extract' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Format' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Helpers' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Replace' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Search' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Text' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Trim' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Token\\Stack' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php', + 'PhpOffice\\PhpSpreadsheet\\Calculation\\Web\\Service' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php', + 'PhpOffice\\PhpSpreadsheet\\CellReferenceHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AddressHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AddressRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\AdvancedValueBinder' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Cell' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\CellAddress' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\CellRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\ColumnRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataType' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataValidation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DataValidator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\DefaultValueBinder' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\Hyperlink' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\IValueBinder' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\IgnoredErrors' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IgnoredErrors.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\RowRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php', + 'PhpOffice\\PhpSpreadsheet\\Cell\\StringValueBinder' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Axis' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\AxisText' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Chart' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\ChartColor' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\DataSeries' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\DataSeriesValues' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Exception' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\GridLines' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Layout' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Legend' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\PlotArea' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\IRenderer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\JpGraph' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\JpGraphRendererBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Renderer\\MtJpGraphRenderer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\Title' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php', + 'PhpOffice\\PhpSpreadsheet\\Chart\\TrendLine' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Cells' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\CellsFactory' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Memory\\SimpleCache1' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php', + 'PhpOffice\\PhpSpreadsheet\\Collection\\Memory\\SimpleCache3' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php', + 'PhpOffice\\PhpSpreadsheet\\Comment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php', + 'PhpOffice\\PhpSpreadsheet\\DefinedName' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php', + 'PhpOffice\\PhpSpreadsheet\\Document\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Document\\Security' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php', + 'PhpOffice\\PhpSpreadsheet\\Exception' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\HashTable' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Dimension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Downloader' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Handler' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Html' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Sample' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\Size' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php', + 'PhpOffice\\PhpSpreadsheet\\Helper\\TextGrid' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php', + 'PhpOffice\\PhpSpreadsheet\\IComparable' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php', + 'PhpOffice\\PhpSpreadsheet\\IOFactory' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php', + 'PhpOffice\\PhpSpreadsheet\\NamedFormula' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php', + 'PhpOffice\\PhpSpreadsheet\\NamedRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\BaseReader' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Csv' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Csv\\Delimiter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\DefaultReadFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Exception' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\PageSetup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Gnumeric\\Styles' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Html' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\IReadFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\IReader' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\AutoFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\BaseLoader' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\DefinedNames' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\FormulaTranslator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\PageSettings' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Ods\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Security\\XmlScanner' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Slk' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BIFF5' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BIFF8' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Color\\BuiltIn' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\ConditionalFormatting' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\DataValidationHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\ErrorCode' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Escher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\MD5' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\RC4' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\Border' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\CellAlignment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\CellFont' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xls\\Style\\FillPattern' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\AutoFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\BaseParserClass' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Chart' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\ColumnAndRowAttributes' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\ConditionalStyles' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\DataValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Hyperlinks' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Namespaces' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\PageSetup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SharedFormula' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SheetViewOptions' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\SheetViews' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Styles' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\TableReader' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\Theme' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xlsx\\WorkbookView' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\DataValidations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\PageSettings' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Properties' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Alignment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Border' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Fill' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\Font' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\NumberFormat' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php', + 'PhpOffice\\PhpSpreadsheet\\Reader\\Xml\\Style\\StyleBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php', + 'PhpOffice\\PhpSpreadsheet\\ReferenceHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\ITextElement' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\RichText' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\Run' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php', + 'PhpOffice\\PhpSpreadsheet\\RichText\\TextElement' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php', + 'PhpOffice\\PhpSpreadsheet\\Settings' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\CodePage' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Date' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Drawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer\\SpgrContainer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DgContainer\\SpgrContainer\\SpContainer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer\\BSE' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Escher\\DggContainer\\BstoreContainer\\BSE\\Blip' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\File' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Font' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\IntOrFloat' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLERead' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\ChainedBlockStream' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS\\File' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS\\Root' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\PasswordHasher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\StringHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\TimeZone' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\BestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\ExponentialBestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\LinearBestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\LogarithmicBestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\PolynomialBestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\PowerBestFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\Trend' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\XMLWriter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Shared\\Xls' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Spreadsheet' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Alignment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Border' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Borders' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Color' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Conditional' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\CellMatcher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\CellStyleAssessor' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalColorScale' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalDataBar' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalDataBarExtension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalFormatValueObject' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\ConditionalFormattingRuleExtension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\StyleMerger' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Blanks' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\CellValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\DateValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Duplicates' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Errors' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\Expression' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\TextValue' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\WizardAbstract' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\ConditionalFormatting\\Wizard\\WizardInterface' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Fill' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Font' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\BaseFormatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\DateFormatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Formatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\FractionFormatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\NumberFormatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\PercentageFormatter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Accounting' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Currency' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Date' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\DateTime' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\DateTimeWizard' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Duration' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Locale' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Number' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Number.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\NumberBase' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Percentage' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Percentage.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Scientific' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Scientific.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Time' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Time.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Wizard' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Wizard.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Protection' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\RgbTint' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/RgbTint.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Style' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Style\\Supervisor' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php', + 'PhpOffice\\PhpSpreadsheet\\Theme' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter\\Column' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFilter\\Column\\Rule' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\AutoFit' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFit.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\BaseDrawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\CellIterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Column' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnCellIterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnDimension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ColumnIterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Dimension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Drawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Drawing\\Shadow' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\HeaderFooter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\HeaderFooterDrawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Iterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\MemoryDrawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageBreak' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageBreak.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageMargins' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\PageSetup' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Pane' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Pane.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\ProtectedRange' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ProtectedRange.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Protection' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Row' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowCellIterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowDimension' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\RowIterator' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\SheetView' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table\\Column' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Table\\TableStyle' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Validations' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php', + 'PhpOffice\\PhpSpreadsheet\\Worksheet\\Worksheet' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\BaseWriter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Csv' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Exception' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Html' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\IWriter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\AutoFilters' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Cell\\Comment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Cell\\Style' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Content' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Formula' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Meta' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\MetaInf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Mimetype' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\NamedExpressions' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Settings' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Styles' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\Thumbnails' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Ods\\WriterPart' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Dompdf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Mpdf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Tcpdf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\BIFFwriter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\CellDataValidation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\ConditionalHelper' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\ErrorCode' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Escher' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Font' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Parser' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellAlignment' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellBorder' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\CellFill' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Style\\ColorMap' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Workbook' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Worksheet' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Xf' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\AutoFilter' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Chart' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Comments' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\ContentTypes' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\DefinedNames' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\DocProps' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Drawing' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\FunctionPrefix' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Rels' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\RelsRibbon' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\RelsVBA' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\StringTable' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Style' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Table' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Theme' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Workbook' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Worksheet' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\WriterPart' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream0' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream0.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream2' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream2.php', + 'PhpOffice\\PhpSpreadsheet\\Writer\\ZipStream3' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream3.php', + 'Psr\\Http\\Client\\ClientExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/ClientExceptionInterface.php', + 'Psr\\Http\\Client\\ClientInterface' => __DIR__ . '/..' . '/psr/http-client/src/ClientInterface.php', + 'Psr\\Http\\Client\\NetworkExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/NetworkExceptionInterface.php', + 'Psr\\Http\\Client\\RequestExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/RequestExceptionInterface.php', + 'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php', + 'Psr\\Http\\Message\\RequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/RequestFactoryInterface.php', + 'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php', + 'Psr\\Http\\Message\\ResponseFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ResponseFactoryInterface.php', + 'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php', + 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ServerRequestFactoryInterface.php', + 'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php', + 'Psr\\Http\\Message\\StreamFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/StreamFactoryInterface.php', + 'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php', + 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UploadedFileFactoryInterface.php', + 'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php', + 'Psr\\Http\\Message\\UriFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UriFactoryInterface.php', + 'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php', + 'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php', + 'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php', + 'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php', + 'Request' => __DIR__ . '/../..' . '/src/Core/Request.php', + 'Response' => __DIR__ . '/../..' . '/src/Core/Response.php', + 'Router' => __DIR__ . '/../..' . '/src/Core/Router.php', + 'Session' => __DIR__ . '/../..' . '/src/Core/Session.php', + 'ZipStream\\CentralDirectoryFileHeader' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php', + 'ZipStream\\CompressionMethod' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/CompressionMethod.php', + 'ZipStream\\DataDescriptor' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/DataDescriptor.php', + 'ZipStream\\EndOfCentralDirectory' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/EndOfCentralDirectory.php', + 'ZipStream\\Exception' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception.php', + 'ZipStream\\Exception\\DosTimeOverflowException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php', + 'ZipStream\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/FileNotFoundException.php', + 'ZipStream\\Exception\\FileNotReadableException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/FileNotReadableException.php', + 'ZipStream\\Exception\\FileSizeIncorrectException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php', + 'ZipStream\\Exception\\OverflowException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/OverflowException.php', + 'ZipStream\\Exception\\ResourceActionException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/ResourceActionException.php', + 'ZipStream\\Exception\\SimulationFileUnknownException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php', + 'ZipStream\\Exception\\StreamNotReadableException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php', + 'ZipStream\\Exception\\StreamNotSeekableException' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php', + 'ZipStream\\File' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/File.php', + 'ZipStream\\GeneralPurposeBitFlag' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php', + 'ZipStream\\LocalFileHeader' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/LocalFileHeader.php', + 'ZipStream\\OperationMode' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/OperationMode.php', + 'ZipStream\\PackField' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/PackField.php', + 'ZipStream\\Time' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Time.php', + 'ZipStream\\Version' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Version.php', + 'ZipStream\\Zip64\\DataDescriptor' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Zip64/DataDescriptor.php', + 'ZipStream\\Zip64\\EndOfCentralDirectory' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php', + 'ZipStream\\Zip64\\EndOfCentralDirectoryLocator' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php', + 'ZipStream\\Zip64\\ExtendedInformationExtraField' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php', + 'ZipStream\\ZipStream' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/ZipStream.php', + 'ZipStream\\Zs\\ExtendedInformationExtraField' => __DIR__ . '/..' . '/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/api/vendor/composer/installed.json b/api/vendor/composer/installed.json index 6a697b3c..f5cf0fba 100644 --- a/api/vendor/composer/installed.json +++ b/api/vendor/composer/installed.json @@ -1,18 +1,294 @@ { "packages": [ { - "name": "phpmailer/phpmailer", - "version": "v6.9.3", - "version_normalized": "6.9.3.0", + "name": "composer/pcre", + "version": "3.3.2", + "version_normalized": "3.3.2.0", "source": { "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "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" + }, + "time": "2024-11-12T16:29:46+00:00", + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "installation-source": "dist", + "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" + } + ], + "install-path": "./pcre" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "version_normalized": "3.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "time": "2025-01-27T12:07:53+00:00", + "type": "library", + "installation-source": "dist", + "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.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "install-path": "../maennchen/zipstream-php" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "version_normalized": "3.0.2.0", + "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" + }, + "time": "2022-12-06T16:21:08+00:00", + "type": "library", + "installation-source": "dist", + "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" + }, + "install-path": "../markbaker/complex" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "version_normalized": "3.0.1.0", + "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" + }, + "time": "2022-12-02T22:17:43+00:00", + "type": "library", + "installation-source": "dist", + "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" + }, + "install-path": "../markbaker/matrix" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.10.0", + "version_normalized": "6.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", "shasum": "" }, "require": { @@ -42,7 +318,7 @@ "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" }, - "time": "2024-11-24T18:04:13+00:00", + "time": "2025-04-24T15:19:31+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -74,7 +350,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" }, "funding": [ { @@ -83,6 +359,337 @@ } ], "install-path": "../phpmailer/phpmailer" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "2.3.8", + "version_normalized": "2.3.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "7a700683743bf1c4a21837c84b266916f1aa7d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/7a700683743bf1c4a21837c84b266916f1aa7d25", + "reference": "7a700683743bf1c4a21837c84b266916f1aa7d25", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "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/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.6 || ^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", + "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" + }, + "time": "2025-02-08T03:01:45+00:00", + "type": "library", + "installation-source": "dist", + "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" + } + ], + "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/2.3.8" + }, + "install-path": "../phpoffice/phpspreadsheet" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "version_normalized": "1.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "time": "2023-09-23T14:17:50+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "install-path": "../psr/http-client" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "time": "2024-04-15T12:06:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "install-path": "../psr/http-factory" + }, + { + "name": "psr/http-message", + "version": "2.0", + "version_normalized": "2.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "time": "2023-04-04T09:54:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "install-path": "../psr/http-message" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "version_normalized": "3.0.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" + }, + "time": "2021-10-29T13:26:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "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" + }, + "install-path": "../psr/simple-cache" } ], "dev": true, diff --git a/api/vendor/composer/installed.php b/api/vendor/composer/installed.php index 21e851b4..e456cf50 100644 --- a/api/vendor/composer/installed.php +++ b/api/vendor/composer/installed.php @@ -3,26 +3,107 @@ 'name' => 'your-vendor/api', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '254f026824de5b6ef6183b0885c1512d32a5d78c', + 'reference' => 'b9672a62283414a30f1bf111ed54759be7392347', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => true, ), 'versions' => array( + 'composer/pcre' => array( + 'pretty_version' => '3.3.2', + 'version' => '3.3.2.0', + 'reference' => 'b2bed4734f0cc156ee1fe9c0da2550420d99a21e', + 'type' => 'library', + 'install_path' => __DIR__ . '/./pcre', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'maennchen/zipstream-php' => array( + 'pretty_version' => '3.1.2', + 'version' => '3.1.2.0', + 'reference' => 'aeadcf5c412332eb426c0f9b4485f6accba2a99f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../maennchen/zipstream-php', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'markbaker/complex' => array( + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', + 'reference' => '95c56caa1cf5c766ad6d65b6344b807c1e8405b9', + 'type' => 'library', + 'install_path' => __DIR__ . '/../markbaker/complex', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'markbaker/matrix' => array( + 'pretty_version' => '3.0.1', + 'version' => '3.0.1.0', + 'reference' => '728434227fe21be27ff6d86621a1b13107a2562c', + 'type' => 'library', + 'install_path' => __DIR__ . '/../markbaker/matrix', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'phpmailer/phpmailer' => array( - 'pretty_version' => 'v6.9.3', - 'version' => '6.9.3.0', - 'reference' => '2f5c94fe7493efc213f643c23b1b1c249d40f47e', + 'pretty_version' => 'v6.10.0', + 'version' => '6.10.0.0', + 'reference' => 'bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144', 'type' => 'library', 'install_path' => __DIR__ . '/../phpmailer/phpmailer', 'aliases' => array(), 'dev_requirement' => false, ), + 'phpoffice/phpspreadsheet' => array( + 'pretty_version' => '2.3.8', + 'version' => '2.3.8.0', + 'reference' => '7a700683743bf1c4a21837c84b266916f1aa7d25', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-client' => array( + 'pretty_version' => '1.0.3', + 'version' => '1.0.3.0', + 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-client', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-factory' => array( + 'pretty_version' => '1.1.0', + 'version' => '1.1.0.0', + 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-factory', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-message' => array( + 'pretty_version' => '2.0', + 'version' => '2.0.0.0', + 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-message', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/simple-cache' => array( + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/simple-cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'your-vendor/api' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '254f026824de5b6ef6183b0885c1512d32a5d78c', + 'reference' => 'b9672a62283414a30f1bf111ed54759be7392347', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/api/vendor/composer/pcre/LICENSE b/api/vendor/composer/pcre/LICENSE new file mode 100644 index 00000000..c5a282ff --- /dev/null +++ b/api/vendor/composer/pcre/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2021 Composer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/api/vendor/composer/pcre/README.md b/api/vendor/composer/pcre/README.md new file mode 100644 index 00000000..49065149 --- /dev/null +++ b/api/vendor/composer/pcre/README.md @@ -0,0 +1,189 @@ +composer/pcre +============= + +PCRE wrapping library that offers type-safe `preg_*` replacements. + +This library gives you a way to ensure `preg_*` functions do not fail silently, returning +unexpected `null`s that may not be handled. + +As of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage +for all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null) +to understand the implications. + +It thus makes it easier to work with static analysis tools like PHPStan or Psalm as it +simplifies and reduces the possible return values from all the `preg_*` functions which +are quite packed with edge cases. As of v2.2.0 / v3.2.0 the library also comes with a +[PHPStan extension](#phpstan-extension) for parsing regular expressions and giving you even better output types. + +This library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations). +If you are looking for a richer API to handle regular expressions have a look at +[rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead. + +[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions) + + +Installation +------------ + +Install the latest version with: + +```bash +$ composer require composer/pcre +``` + + +Requirements +------------ + +* PHP 7.4.0 is required for 3.x versions +* PHP 7.2.0 is required for 2.x versions +* PHP 5.3.2 is required for 1.x versions + + +Basic usage +----------- + +Instead of: + +```php +if (preg_match('{fo+}', $string, $matches)) { ... } +if (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... } +if (preg_match_all('{fo+}', $string, $matches)) { ... } +$newString = preg_replace('{fo+}', 'bar', $string); +$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = preg_replace_callback_array(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = preg_grep('{[a-z]}', $elements); +$array = preg_split('{[a-z]+}', $string); +``` + +You can now call these on the `Preg` class: + +```php +use Composer\Pcre\Preg; + +if (Preg::match('{fo+}', $string, $matches)) { ... } +if (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... } +if (Preg::matchAll('{fo+}', $string, $matches)) { ... } +$newString = Preg::replace('{fo+}', 'bar', $string); +$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = Preg::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = Preg::grep('{[a-z]}', $elements); +$array = Preg::split('{[a-z]+}', $string); +``` + +The main difference is if anything fails to match/replace/.., it will throw a `Composer\Pcre\PcreException` +instead of returning `null` (or false in some cases), so you can now use the return values safely relying on +the fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split). + +Additionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety +when the number of pattern matches is not useful: + +```php +use Composer\Pcre\Preg; + +if (Preg::isMatch('{fo+}', $string, $matches)) // bool +if (Preg::isMatchAll('{fo+}', $string, $matches)) // bool +``` + +Finally the `Preg` class provides a few `*StrictGroups` method variants that ensure match groups +are always present and thus non-nullable, making it easier to write type-safe code: + +```php +use Composer\Pcre\Preg; + +// $matches is guaranteed to be an array of strings, if a subpattern does not match and produces a null it will throw +if (Preg::matchStrictGroups('{fo+}', $string, $matches)) +if (Preg::matchAllStrictGroups('{fo+}', $string, $matches)) +``` + +**Note:** This is generally safe to use as long as you do not have optional subpatterns (i.e. `(something)?` +or `(something)*` or branches with a `|` that result in some groups not being matched at all). +A subpattern that can match an empty string like `(.*)` is **not** optional, it will be present as an +empty string in the matches. A non-matching subpattern, even if optional like `(?:foo)?` will anyway not be present in +matches so it is also not a problem to use these with `*StrictGroups` methods. + +If you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class: + +```php +use Composer\Pcre\Regex; + +// this is useful when you are just interested in knowing if something matched +// as it returns a bool instead of int(1/0) for match +$bool = Regex::isMatch('{fo+}', $string); + +$result = Regex::match('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchWithOffsets('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchAll('{fo+}', $string); +if ($result->matched && $result->count > 3) { something($result->matches); } + +$newString = Regex::replace('{fo+}', 'bar', $string)->result; +$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)->result; +$newString = Regex::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string)->result; +``` + +Note that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have +complex return types warranting a specific result object. + +See the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php), +[MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details. + +Restrictions / Limitations +-------------------------- + +Due to type safety requirements a few restrictions are in place. + +- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`. + You cannot pass the flag to `match`/`matchAll`. +- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets` + instead. +- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There + is no alternative provided as you can fairly easily code around it. +- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather + use `Preg::grep` in combination with some loop and `Preg::replace`. +- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`, + only simple strings. +- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much + saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for + `replaceCallback` and `replaceCallbackArray`. + +#### PREG_UNMATCHED_AS_NULL + +As of 2.0, this library always uses PREG_UNMATCHED_AS_NULL for all `match*` and `isMatch*` +functions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`. + +This means your matches will always contain all matching groups, either as null if unmatched +or as string if it matched. + +The advantages in clarity and predictability are clearer if you compare the two outputs of +running this with and without PREG_UNMATCHED_AS_NULL in $flags: + +```php +preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags); +``` + +| no flag | PREG_UNMATCHED_AS_NULL | +| --- | --- | +| array (size=4) | array (size=5) | +| 0 => string 'ac' (length=2) | 0 => string 'ac' (length=2) | +| 1 => string 'a' (length=1) | 1 => string 'a' (length=1) | +| 2 => string '' (length=0) | 2 => null | +| 3 => string 'c' (length=1) | 3 => string 'c' (length=1) | +| | 4 => null | +| group 2 (any unmatched group preceding one that matched) is set to `''`. You cannot tell if it matched an empty string or did not match at all | group 2 is `null` when unmatched and a string if it matched, easy to check for | +| group 4 (any optional group without a matching one following) is missing altogether. So you have to check with `isset()`, but really you want `isset($m[4]) && $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows it | group 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null` | + +PHPStan Extension +----------------- + +To use the PHPStan extension if you do not use `phpstan/extension-installer` you can include `vendor/composer/pcre/extension.neon` in your PHPStan config. + +The extension provides much better type information for $matches as well as regex validation where possible. + +License +------- + +composer/pcre is licensed under the MIT License, see the LICENSE file for details. diff --git a/api/vendor/composer/pcre/composer.json b/api/vendor/composer/pcre/composer.json new file mode 100644 index 00000000..d3a7e67c --- /dev/null +++ b/api/vendor/composer/pcre/composer.json @@ -0,0 +1,54 @@ +{ + "name": "composer/pcre", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "type": "library", + "license": "MIT", + "keywords": [ + "pcre", + "regex", + "preg", + "regular expression" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8 || ^9", + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Pcre\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "scripts": { + "test": "@php vendor/bin/phpunit", + "phpstan": "@php phpstan analyse" + } +} diff --git a/api/vendor/composer/pcre/extension.neon b/api/vendor/composer/pcre/extension.neon new file mode 100644 index 00000000..b9cea113 --- /dev/null +++ b/api/vendor/composer/pcre/extension.neon @@ -0,0 +1,22 @@ +# composer/pcre PHPStan extensions +# +# These can be reused by third party packages by including 'vendor/composer/pcre/extension.neon' +# in your phpstan config + +services: + - + class: Composer\Pcre\PHPStan\PregMatchParameterOutTypeExtension + tags: + - phpstan.staticMethodParameterOutTypeExtension + - + class: Composer\Pcre\PHPStan\PregMatchTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension + - + class: Composer\Pcre\PHPStan\PregReplaceCallbackClosureTypeExtension + tags: + - phpstan.staticMethodParameterClosureTypeExtension + +rules: + - Composer\Pcre\PHPStan\UnsafeStrictGroupsCallRule + - Composer\Pcre\PHPStan\InvalidRegexPatternRule diff --git a/api/vendor/composer/pcre/src/MatchAllResult.php b/api/vendor/composer/pcre/src/MatchAllResult.php new file mode 100644 index 00000000..b22b52d6 --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchAllResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllResult +{ + /** + * An array of match group => list of matched strings + * + * @readonly + * @var array> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/api/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php b/api/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php new file mode 100644 index 00000000..b7ec3974 --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllStrictGroupsResult +{ + /** + * An array of match group => list of matched strings + * + * @readonly + * @var array> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/api/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php b/api/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php new file mode 100644 index 00000000..032a02cd --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllWithOffsetsResult +{ + /** + * An array of match group => list of matches, every match being a pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array> + * @phpstan-var array}>> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + * @phpstan-param array}>> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/api/vendor/composer/pcre/src/MatchResult.php b/api/vendor/composer/pcre/src/MatchResult.php new file mode 100644 index 00000000..e951a5ee --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchResult.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchResult +{ + /** + * An array of match group => string matched + * + * @readonly + * @var array + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/api/vendor/composer/pcre/src/MatchStrictGroupsResult.php b/api/vendor/composer/pcre/src/MatchStrictGroupsResult.php new file mode 100644 index 00000000..126ee629 --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchStrictGroupsResult.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchStrictGroupsResult +{ + /** + * An array of match group => string matched + * + * @readonly + * @var array + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/api/vendor/composer/pcre/src/MatchWithOffsetsResult.php b/api/vendor/composer/pcre/src/MatchWithOffsetsResult.php new file mode 100644 index 00000000..ba4d4bc4 --- /dev/null +++ b/api/vendor/composer/pcre/src/MatchWithOffsetsResult.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchWithOffsetsResult +{ + /** + * An array of match group => pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array + * @phpstan-var array}> + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + * @phpstan-param array}> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/api/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php b/api/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php new file mode 100644 index 00000000..8a05fb24 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php @@ -0,0 +1,142 @@ + + */ +class InvalidRegexPatternRule implements Rule +{ + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $patterns = $this->extractPatterns($node, $scope); + + $errors = []; + foreach ($patterns as $pattern) { + $errorMessage = $this->validatePattern($pattern); + if ($errorMessage === null) { + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf('Regex pattern is invalid: %s', $errorMessage))->identifier('regexp.pattern')->build(); + } + + return $errors; + } + + /** + * @return string[] + */ + private function extractPatterns(StaticCall $node, Scope $scope): array + { + if (!$node->class instanceof FullyQualified) { + return []; + } + $isRegex = $node->class->toString() === Regex::class; + $isPreg = $node->class->toString() === Preg::class; + if (!$isRegex && !$isPreg) { + return []; + } + if (!$node->name instanceof Node\Identifier || !Preg::isMatch('{^(match|isMatch|grep|replace|split)}', $node->name->name)) { + return []; + } + + $functionName = $node->name->name; + if (!isset($node->getArgs()[0])) { + return []; + } + + $patternNode = $node->getArgs()[0]->value; + $patternType = $scope->getType($patternNode); + + $patternStrings = []; + + foreach ($patternType->getConstantStrings() as $constantStringType) { + if ($functionName === 'replaceCallbackArray') { + continue; + } + + $patternStrings[] = $constantStringType->getValue(); + } + + foreach ($patternType->getConstantArrays() as $constantArrayType) { + if ( + in_array($functionName, [ + 'replace', + 'replaceCallback', + ], true) + ) { + foreach ($constantArrayType->getValueTypes() as $arrayKeyType) { + foreach ($arrayKeyType->getConstantStrings() as $constantString) { + $patternStrings[] = $constantString->getValue(); + } + } + } + + if ($functionName !== 'replaceCallbackArray') { + continue; + } + + foreach ($constantArrayType->getKeyTypes() as $arrayKeyType) { + foreach ($arrayKeyType->getConstantStrings() as $constantString) { + $patternStrings[] = $constantString->getValue(); + } + } + } + + return $patternStrings; + } + + private function validatePattern(string $pattern): ?string + { + try { + $msg = null; + $prev = set_error_handler(function (int $severity, string $message, string $file) use (&$msg): bool { + $msg = preg_replace("#^preg_match(_all)?\\(.*?\\): #", '', $message); + + return true; + }); + + if ($pattern === '') { + return 'Empty string is not a valid regular expression'; + } + + Preg::match($pattern, ''); + if ($msg !== null) { + return $msg; + } + } catch (PcreException $e) { + if ($e->getCode() === PREG_INTERNAL_ERROR && $msg !== null) { + return $msg; + } + + return preg_replace('{.*? failed executing ".*": }', '', $e->getMessage()); + } finally { + restore_error_handler(); + } + + return null; + } + +} diff --git a/api/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php b/api/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php new file mode 100644 index 00000000..aa30ab34 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php @@ -0,0 +1,70 @@ +getType($flagsArg->value); + + $constantScalars = $flagsType->getConstantScalarValues(); + if ($constantScalars === []) { + return null; + } + + $internalFlagsTypes = []; + foreach ($flagsType->getConstantScalarValues() as $constantScalarValue) { + if (!is_int($constantScalarValue)) { + return null; + } + + $internalFlagsTypes[] = new ConstantIntegerType($constantScalarValue | PREG_UNMATCHED_AS_NULL); + } + return TypeCombinator::union(...$internalFlagsTypes); + } + + static public function removeNullFromMatches(Type $matchesType): Type + { + return TypeTraverser::map($matchesType, static function (Type $type, callable $traverse): Type { + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return $traverse($type); + } + + if ($type instanceof ConstantArrayType) { + return new ConstantArrayType( + $type->getKeyTypes(), + array_map(static function (Type $valueType) use ($traverse): Type { + return $traverse($valueType); + }, $type->getValueTypes()), + $type->getNextAutoIndexes(), + [], + $type->isList() + ); + } + + if ($type instanceof ArrayType) { + return new ArrayType($type->getKeyType(), $traverse($type->getItemType())); + } + + return TypeCombinator::removeNull($type); + }); + } + +} diff --git a/api/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php b/api/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php new file mode 100644 index 00000000..e0d60208 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php @@ -0,0 +1,65 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool + { + return + $methodReflection->getDeclaringClass()->getName() === Preg::class + && in_array($methodReflection->getName(), [ + 'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups', + 'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups' + ], true) + && $parameter->getName() === 'matches'; + } + + public function getParameterOutTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type + { + $args = $methodCall->getArgs(); + $patternArg = $args[0] ?? null; + $matchesArg = $args[2] ?? null; + $flagsArg = $args[3] ?? null; + + if ( + $patternArg === null || $matchesArg === null + ) { + return null; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return null; + } + + if (stripos($methodReflection->getName(), 'matchAll') !== false) { + return $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); + } + + return $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); + } + +} diff --git a/api/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php b/api/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php new file mode 100644 index 00000000..3db0ce06 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php @@ -0,0 +1,119 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + + public function getClass(): string + { + return Preg::class; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, StaticCall $node, TypeSpecifierContext $context): bool + { + return in_array($methodReflection->getName(), [ + 'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups', + 'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups' + ], true) + && !$context->null(); + } + + public function specifyTypes(MethodReflection $methodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes + { + $args = $node->getArgs(); + $patternArg = $args[0] ?? null; + $matchesArg = $args[2] ?? null; + $flagsArg = $args[3] ?? null; + + if ( + $patternArg === null || $matchesArg === null + ) { + return new SpecifiedTypes(); + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return new SpecifiedTypes(); + } + + if (stripos($methodReflection->getName(), 'matchAll') !== false) { + $matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + } else { + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + } + + if ($matchedType === null) { + return new SpecifiedTypes(); + } + + if ( + in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true) + ) { + $matchedType = PregMatchFlags::removeNullFromMatches($matchedType); + } + + $overwrite = false; + if ($context->false()) { + $overwrite = true; + $context = $context->negate(); + } + + // @phpstan-ignore function.alreadyNarrowedType + if (method_exists('PHPStan\Analyser\SpecifiedTypes', 'setRootExpr')) { + $typeSpecifier = $this->typeSpecifier->create( + $matchesArg->value, + $matchedType, + $context, + $scope + )->setRootExpr($node); + + return $overwrite ? $typeSpecifier->setAlwaysOverwriteTypes() : $typeSpecifier; + } + + // @phpstan-ignore arguments.count + return $this->typeSpecifier->create( + $matchesArg->value, + $matchedType, + $context, + // @phpstan-ignore argument.type + $overwrite, + $scope, + $node + ); + } +} diff --git a/api/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php b/api/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php new file mode 100644 index 00000000..7b953672 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php @@ -0,0 +1,91 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool + { + return in_array($methodReflection->getDeclaringClass()->getName(), [Preg::class, Regex::class], true) + && in_array($methodReflection->getName(), ['replaceCallback', 'replaceCallbackStrictGroups'], true) + && $parameter->getName() === 'replacement'; + } + + public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type + { + $args = $methodCall->getArgs(); + $patternArg = $args[0] ?? null; + $flagsArg = $args[5] ?? null; + + if ( + $patternArg === null + ) { + return null; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + + $matchesType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope); + if ($matchesType === null) { + return null; + } + + if ($methodReflection->getName() === 'replaceCallbackStrictGroups' && count($matchesType->getConstantArrays()) === 1) { + $matchesType = $matchesType->getConstantArrays()[0]; + $matchesType = new ConstantArrayType( + $matchesType->getKeyTypes(), + array_map(static function (Type $valueType): Type { + if (count($valueType->getConstantArrays()) === 1) { + $valueTypeArray = $valueType->getConstantArrays()[0]; + return new ConstantArrayType( + $valueTypeArray->getKeyTypes(), + array_map(static function (Type $valueType): Type { + return TypeCombinator::removeNull($valueType); + }, $valueTypeArray->getValueTypes()), + $valueTypeArray->getNextAutoIndexes(), + [], + $valueTypeArray->isList() + ); + } + return TypeCombinator::removeNull($valueType); + }, $matchesType->getValueTypes()), + $matchesType->getNextAutoIndexes(), + [], + $matchesType->isList() + ); + } + + return new ClosureType( + [ + new NativeParameterReflection($parameter->getName(), $parameter->isOptional(), $matchesType, $parameter->passedByReference(), $parameter->isVariadic(), $parameter->getDefaultValue()), + ], + new StringType() + ); + } +} diff --git a/api/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php b/api/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php new file mode 100644 index 00000000..5bced507 --- /dev/null +++ b/api/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php @@ -0,0 +1,112 @@ + + */ +final class UnsafeStrictGroupsCallRule implements Rule +{ + /** + * @var RegexArrayShapeMatcher + */ + private $regexShapeMatcher; + + public function __construct(RegexArrayShapeMatcher $regexShapeMatcher) + { + $this->regexShapeMatcher = $regexShapeMatcher; + } + + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->class instanceof FullyQualified) { + return []; + } + $isRegex = $node->class->toString() === Regex::class; + $isPreg = $node->class->toString() === Preg::class; + if (!$isRegex && !$isPreg) { + return []; + } + if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)) { + return []; + } + + $args = $node->getArgs(); + if (!isset($args[0])) { + return []; + } + + $patternArg = $args[0] ?? null; + if ($isPreg) { + if (!isset($args[2])) { // no matches set, skip as the matches won't be used anyway + return []; + } + $flagsArg = $args[3] ?? null; + } else { + $flagsArg = $args[2] ?? null; + } + + if ($patternArg === null) { + return []; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return []; + } + + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope); + if ($matchedType === null) { + return [ + RuleErrorBuilder::message(sprintf('The %s call is potentially unsafe as $matches\' type could not be inferred.', $node->name->name)) + ->identifier('composerPcre.maybeUnsafeStrictGroups') + ->build(), + ]; + } + + if (count($matchedType->getConstantArrays()) === 1) { + $matchedType = $matchedType->getConstantArrays()[0]; + $nullableGroups = []; + foreach ($matchedType->getValueTypes() as $index => $type) { + if (TypeCombinator::containsNull($type)) { + $nullableGroups[] = $matchedType->getKeyTypes()[$index]->getValue(); + } + } + + if (\count($nullableGroups) > 0) { + return [ + RuleErrorBuilder::message(sprintf( + 'The %s call is unsafe as match group%s "%s" %s optional and may be null.', + $node->name->name, + \count($nullableGroups) > 1 ? 's' : '', + implode('", "', $nullableGroups), + \count($nullableGroups) > 1 ? 'are' : 'is' + ))->identifier('composerPcre.unsafeStrictGroups')->build(), + ]; + } + } + + return []; + } +} diff --git a/api/vendor/composer/pcre/src/PcreException.php b/api/vendor/composer/pcre/src/PcreException.php new file mode 100644 index 00000000..23d93279 --- /dev/null +++ b/api/vendor/composer/pcre/src/PcreException.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class PcreException extends \RuntimeException +{ + /** + * @param string $function + * @param string|string[] $pattern + * @return self + */ + public static function fromFunction($function, $pattern) + { + $code = preg_last_error(); + + if (is_array($pattern)) { + $pattern = implode(', ', $pattern); + } + + return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code); + } + + /** + * @param int $code + * @return string + */ + private static function pcreLastErrorMessage($code) + { + if (function_exists('preg_last_error_msg')) { + return preg_last_error_msg(); + } + + $constants = get_defined_constants(true); + if (!isset($constants['pcre']) || !is_array($constants['pcre'])) { + return 'UNDEFINED_ERROR'; + } + + foreach ($constants['pcre'] as $const => $val) { + if ($val === $code && substr($const, -6) === '_ERROR') { + return $const; + } + } + + return 'UNDEFINED_ERROR'; + } +} diff --git a/api/vendor/composer/pcre/src/Preg.php b/api/vendor/composer/pcre/src/Preg.php new file mode 100644 index 00000000..400abbfe --- /dev/null +++ b/api/vendor/composer/pcre/src/Preg.php @@ -0,0 +1,430 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Preg +{ + /** @internal */ + public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.'; + /** @internal */ + public const INVALID_TYPE_MSG = '$subject must be a string, %s given.'; + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|1 + * + * @param-out array $matches + */ + public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + self::checkOffsetCapture($flags, 'matchWithOffsets'); + + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * Variant of `match()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|1 + * @throws UnexpectedNullMatchException + * + * @param-out array $matches + */ + public static function matchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + $result = self::match($pattern, $subject, $matchesInternal, $flags, $offset); + $matches = self::enforceNonNullMatches($pattern, $matchesInternal, 'match'); + + return $result; + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_OFFSET_CAPTURE are always set, no other flags are supported + * @return 0|1 + * + * @param-out array}> $matches + */ + public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|positive-int + * + * @param-out array> $matches + */ + public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * Variant of `match()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|positive-int + * @throws UnexpectedNullMatchException + * + * @param-out array> $matches + */ + public static function matchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + $result = self::matchAll($pattern, $subject, $matchesInternal, $flags, $offset); + $matches = self::enforceNonNullMatchAll($pattern, $matchesInternal, 'matchAll'); + + return $result; + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + * @return 0|positive-int + * + * @param-out array}>> $matches + */ + public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + self::checkSetOrder($flags); + + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + * @param int $count Set by method + * + * @param-out int<0, max> $count + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1, ?int &$count = null): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace($pattern, $replacement, $subject, $limit, $count); + if ($result === null) { + throw PcreException::fromFunction('preg_replace', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + throw PcreException::fromFunction('preg_replace_callback', $pattern); + } + + return $result; + } + + /** + * Variant of `replaceCallback()` which outputs non-null matches (or throws) + * + * @param string $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallbackStrictGroups(string $pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + return self::replaceCallback($pattern, function (array $matches) use ($pattern, $replacement) { + return $replacement(self::enforceNonNullMatches($pattern, $matches, 'replaceCallback')); + }, $subject, $limit, $count, $flags); + } + + /** + * @param ($flags is PREG_OFFSET_CAPTURE ? (array}>): string>) : array): string>) $pattern + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + $pattern = array_keys($pattern); + throw PcreException::fromFunction('preg_replace_callback_array', $pattern); + } + + return $result; + } + + /** + * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE + * @return list + */ + public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead'); + } + + $result = preg_split($pattern, $subject, $limit, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE is always set + * @return list + * @phpstan-return list}> + */ + public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + $result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @template T of string|\Stringable + * @param string $pattern + * @param array $array + * @param int-mask $flags PREG_GREP_INVERT + * @return array + */ + public static function grep(string $pattern, array $array, int $flags = 0): array + { + $result = preg_grep($pattern, $array, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_grep', $pattern); + } + + return $result; + } + + /** + * Variant of match() which returns a bool instead of int + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array $matches + */ + public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::match($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of `isMatch()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + * + * @param-out array $matches + */ + public static function isMatchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) self::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchAll() which returns a bool instead of int + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array> $matches + */ + public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of `isMatchAll()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array> $matches + */ + public static function isMatchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) self::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchWithOffsets() which returns a bool instead of int + * + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array}> $matches + */ + public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchAllWithOffsets() which returns a bool instead of int + * + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array}>> $matches + */ + public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + } + + private static function checkOffsetCapture(int $flags, string $useFunctionName): void + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use ' . $useFunctionName . '() instead'); + } + } + + private static function checkSetOrder(int $flags): void + { + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches'); + } + } + + /** + * @param array $matches + * @return array + * @throws UnexpectedNullMatchException + */ + private static function enforceNonNullMatches(string $pattern, array $matches, string $variantMethod) + { + foreach ($matches as $group => $match) { + if (is_string($match) || (is_array($match) && is_string($match[0]))) { + continue; + } + + throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); + } + + /** @var array */ + return $matches; + } + + /** + * @param array> $matches + * @return array> + * @throws UnexpectedNullMatchException + */ + private static function enforceNonNullMatchAll(string $pattern, array $matches, string $variantMethod) + { + foreach ($matches as $group => $groupMatches) { + foreach ($groupMatches as $match) { + if (null === $match) { + throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); + } + } + } + + /** @var array> */ + return $matches; + } +} diff --git a/api/vendor/composer/pcre/src/Regex.php b/api/vendor/composer/pcre/src/Regex.php new file mode 100644 index 00000000..038cf069 --- /dev/null +++ b/api/vendor/composer/pcre/src/Regex.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Regex +{ + /** + * @param non-empty-string $pattern + */ + public static function isMatch(string $pattern, string $subject, int $offset = 0): bool + { + return (bool) Preg::match($pattern, $subject, $matches, 0, $offset); + } + + /** + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult + { + self::checkOffsetCapture($flags, 'matchWithOffsets'); + + $count = Preg::match($pattern, $subject, $matches, $flags, $offset); + + return new MatchResult($count, $matches); + } + + /** + * Variant of `match()` which returns non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + */ + public static function matchStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchStrictGroupsResult + { + // @phpstan-ignore composerPcre.maybeUnsafeStrictGroups + $count = Preg::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); + + return new MatchStrictGroupsResult($count, $matches); + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult + { + $count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchWithOffsetsResult($count, $matches); + } + + /** + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + $count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllResult($count, $matches); + } + + /** + * Variant of `matchAll()` which returns non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + */ + public static function matchAllStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllStrictGroupsResult + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + // @phpstan-ignore composerPcre.maybeUnsafeStrictGroups + $count = Preg::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllStrictGroupsResult($count, $matches); + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult + { + self::checkSetOrder($flags); + + $count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllWithOffsetsResult($count, $matches); + } + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult + { + $result = Preg::replace($pattern, $replacement, $subject, $limit, $count); + + return new ReplaceResult($count, $result); + } + + /** + * @param string|string[] $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + /** + * Variant of `replaceCallback()` which outputs non-null matches (or throws) + * + * @param string $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackStrictGroups($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallbackStrictGroups($pattern, $replacement, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + /** + * @param ($flags is PREG_OFFSET_CAPTURE ? (array}>): string>) : array): string>) $pattern + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + private static function checkOffsetCapture(int $flags, string $useFunctionName): void + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use '.$useFunctionName.'() instead'); + } + } + + private static function checkSetOrder(int $flags): void + { + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type'); + } + } +} diff --git a/api/vendor/composer/pcre/src/ReplaceResult.php b/api/vendor/composer/pcre/src/ReplaceResult.php new file mode 100644 index 00000000..33847712 --- /dev/null +++ b/api/vendor/composer/pcre/src/ReplaceResult.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class ReplaceResult +{ + /** + * @readonly + * @var string + */ + public $result; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + */ + public function __construct(int $count, string $result) + { + $this->count = $count; + $this->matched = (bool) $count; + $this->result = $result; + } +} diff --git a/api/vendor/composer/pcre/src/UnexpectedNullMatchException.php b/api/vendor/composer/pcre/src/UnexpectedNullMatchException.php new file mode 100644 index 00000000..f123828b --- /dev/null +++ b/api/vendor/composer/pcre/src/UnexpectedNullMatchException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class UnexpectedNullMatchException extends PcreException +{ + public static function fromFunction($function, $pattern) + { + throw new \LogicException('fromFunction should not be called on '.self::class.', use '.PcreException::class); + } +} diff --git a/api/vendor/composer/platform_check.php b/api/vendor/composer/platform_check.php deleted file mode 100644 index 4c3a5d68..00000000 --- a/api/vendor/composer/platform_check.php +++ /dev/null @@ -1,26 +0,0 @@ -= 80100)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.'; -} - -if ($issues) { - if (!headers_sent()) { - header('HTTP/1.1 500 Internal Server Error'); - } - if (!ini_get('display_errors')) { - if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { - fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); - } elseif (!headers_sent()) { - echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; - } - } - trigger_error( - 'Composer detected issues in your platform: ' . implode(' ', $issues), - E_USER_ERROR - ); -} diff --git a/api/vendor/maennchen/zipstream-php/.editorconfig b/api/vendor/maennchen/zipstream-php/.editorconfig new file mode 100644 index 00000000..f7cd9142 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.editorconfig @@ -0,0 +1,22 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{yml,md,xml}] +indent_style = space +indent_size = 2 + +[*.{rst,php}] +indent_style = space +indent_size = 4 + +[composer.json] +indent_style = space +indent_size = 2 + +[composer.lock] +indent_style = space +indent_size = 4 diff --git a/api/vendor/maennchen/zipstream-php/.gitattributes b/api/vendor/maennchen/zipstream-php/.gitattributes new file mode 100644 index 00000000..e058ebd0 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.gitattributes @@ -0,0 +1,6 @@ +.gitignore text eol=lf +.gitattributes text eol=lf +*.md text eol=lf +*.php text eol=lf +*.yml text eol=lf +*.xml text eol=lf diff --git a/api/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md b/api/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9d75b876 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +jonatan@maennchen.ch. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/api/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md b/api/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md new file mode 100644 index 00000000..d8caee08 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md @@ -0,0 +1,139 @@ +# Contributing to ZipStream-PHP + +## Welcome! + +We look forward to your contributions! Here are some examples how you can +contribute: + +- [Report a bug](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=bug&template=BUG.md) +- [Propose a new feature](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=enhancement&template=FEATURE.md) +- [Send a pull request](https://github.com/maennchen/ZipStream-PHP/pulls) + +## We have a Code of Conduct + +Please note that this project is released with a +[Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this +project you agree to abide by its terms. + +## Any contributions you make will be under the MIT License + +When you submit code changes, your submissions are understood to be under the +same [MIT License](https://github.com/maennchen/ZipStream-PHP/blob/main/LICENSE) +that covers the project. By contributing to this project, you agree that your +contributions will be licensed under its MIT License. + +## Write bug reports with detail, background, and sample code + +In your bug report, please provide the following: + +- A quick summary and/or background +- Steps to reproduce + - Be specific! + - Give sample code if you can. +- What you expected would happen +- What actually happens +- Notes (possibly including why you think this might be happening, or stuff you +- tried that didn't work) + +Please do not report a bug for a version of ZIPStream-PHP that is no longer +supported (`< 3.0.0`). Please do not report a bug if you are using a version of +PHP that is not supported by the version of ZipStream-PHP you are using. + +Please post code and output as text +([using proper markup](https://guides.github.com/features/mastering-markdown/)). +Do not post screenshots of code or output. + +Please include the output of `composer info | sort`. + +## Workflow for Pull Requests + +1. Fork the repository. +2. Create your branch from `main` if you plan to implement new functionality or + change existing code significantly; create your branch from the oldest branch + that is affected by the bug if you plan to fix a bug. +3. Implement your change and add tests for it. +4. Ensure the test suite passes. +5. Ensure the code complies with our coding guidelines (see below). +6. Send that pull request! + +Please make sure you have +[set up your user name and email address](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup) +for use with Git. Strings such as `silly nick name ` look really +stupid in the commit history of a project. + +We encourage you to +[sign your Git commits with your GPG key](https://docs.github.com/en/github/authenticating-to-github/signing-commits). + +Pull requests for new features must be based on the `main` branch. + +We are trying to keep backwards compatibility breaks in ZipStream-PHP to a +minimum. Please take this into account when proposing changes. + +Due to time constraints, we are not always able to respond as quickly as we +would like. Please do not take delays personal and feel free to remind us if you +feel that we forgot to respond. + +## Coding Guidelines + +This project comes with a configuration file (located at `/psalm.yml` in the +repository) that you can use to perform static analysis (with a focus on type +checking): + +```bash +$ .composer run test:lint +``` + +This project comes with a configuration file (located at +`/.php-cs-fixer.dist.php` in the repository) that you can use to (re)format your +source code for compliance with this project's coding guidelines: + +```bash +$ composer run format +``` + +Please understand that we will not accept a pull request when its changes +violate this project's coding guidelines. + +## Using ZipStream-PHP from a Git checkout + +The following commands can be used to perform the initial checkout of +ZipStream-PHP: + +```bash +$ git clone git@github.com:maennchen/ZipStream-PHP.git + +$ cd ZipStream-PHP +``` + +Install ZipStream-PHP's dependencies using [Composer](https://getcomposer.org/): + +```bash +$ composer install +$ composer run install:tools # Install phpDocumentor using phive +``` + +## Running ZipStream-PHP's test suite + +After following the steps shown above, ZipStream-PHP's test suite is run like +this: + +```bash +$ composer run test:unit +``` + +There's some slow tests in the test suite that test the handling of big files in +the archives. To skip them use the following command instead: + +```bash +$ composer run test:unit:fast +``` + +## Generating ZipStream-PHP Documentation + +To generate the documentation for the library, run: + +```bash +$ composer run docs:generate +``` + +The guide documentation pages can be found in the `/guides/` directory. diff --git a/api/vendor/maennchen/zipstream-php/.github/FUNDING.yml b/api/vendor/maennchen/zipstream-php/.github/FUNDING.yml new file mode 100644 index 00000000..5a461276 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/FUNDING.yml @@ -0,0 +1 @@ +github: maennchen diff --git a/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml b/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml new file mode 100644 index 00000000..0eb8cc77 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml @@ -0,0 +1,71 @@ +name: 🐞 Bug Report +description: Something is broken? +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + - Create a discussion instead if you are looking for support: + https://github.com/maennchen/ZipStream-PHP/discussions + - type: input + id: version + attributes: + label: ZipStream-PHP version + placeholder: x.y.z + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP version + placeholder: x.y.z + validations: + required: true + - type: checkboxes + id: constraints + attributes: + label: Constraints for Bug Report + options: + - label: | + I'm using a version of ZipStream that is currently supported: + https://github.com/maennchen/ZipStream-PHP#version-support + required: true + - label: | + I'm using a version of PHP that has active support: + https://www.php.net/supported-versions.php + required: true + - label: | + I'm using a version of PHP that is compatible with your used + ZipStream version. + required: true + - label: | + I'm using the latest release of the used ZipStream major version. + required: true + - type: textarea + id: summary + attributes: + label: Summary + description: Provide a summary describing the problem you are experiencing. + validations: + required: true + - type: textarea + id: current-behaviour + attributes: + label: Current behavior + description: What is the current (buggy) behavior? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: How to reproduce + description: Provide steps to reproduce the bug. + validations: + required: true + - type: textarea + id: expected-behaviour + attributes: + label: Expected behavior + description: What was the expected (correct) behavior? + validations: + required: true diff --git a/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml b/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml new file mode 100644 index 00000000..e5dec637 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml @@ -0,0 +1,11 @@ +name: 🎉 Feature Request +description: You have a neat idea that should be implemented? +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: Description + description: Provide a summary of the feature you would like to see implemented. + validations: + required: true diff --git a/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..6892c571 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +Please go the the `Preview` tab and select the appropriate sub-template: + +* [🐞 Failing Test](?expand=1&template=FAILING_TEST.md) +* [🐞 Bug Fix](?expand=1&template=FIX.md) +* [⚙ Improvement](?expand=1&template=IMPROVEMENT.md) +* [🎉 New Feature](?expand=1&template=NEW_FEATURE.md) diff --git a/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md new file mode 100644 index 00000000..24603cb6 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md @@ -0,0 +1,13 @@ + + + diff --git a/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md new file mode 100644 index 00000000..77f65a08 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md @@ -0,0 +1,13 @@ + + + diff --git a/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md new file mode 100644 index 00000000..3ac8e310 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md @@ -0,0 +1,9 @@ + + + diff --git a/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md new file mode 100644 index 00000000..ca53939c --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md @@ -0,0 +1,9 @@ + + + diff --git a/api/vendor/maennchen/zipstream-php/.github/SECURITY.md b/api/vendor/maennchen/zipstream-php/.github/SECURITY.md new file mode 100644 index 00000000..3046c310 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/SECURITY.md @@ -0,0 +1,22 @@ +# Security Policy + +[![OpenSSF Vulnerability Disclosure](https://img.shields.io/badge/OpenSSF-Vulnerability_Disclosure-green)](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md) +[![GitHub Report](https://img.shields.io/badge/GitHub-Security_Advisories-blue)](https://github.com/maennchen/ZipStream-PHP/security/advisories/new) +[![Email Report](https://img.shields.io/badge/Email-jonatan%40maennchen.ch-blue)](mailto:jonatan@maennchen.ch) + +This repository follows the +[OpenSSF Vulnerability Disclosure guide](https://github.com/ossf/oss-vulnerability-guide/tree/main). +You can learn more about it in the +[Finders Guide](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md). + +Please report vulnerabilities via the +[GitHub Security Vulnerability Reporting](https://github.com/maennchen/ZipStream-PHP/security/advisories/new) +or via email to [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch) if this does +not work for you. + +Our vulnerability management team will respond within 3 working days of your +report. If the issue is confirmed as a vulnerability, we will open a Security +Advisory. This project follows a 90 day disclosure timeline. + +If you have questions about reporting security issues, email the vulnerability +management team: [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch) diff --git a/api/vendor/maennchen/zipstream-php/.github/dependabot.yml b/api/vendor/maennchen/zipstream-php/.github/dependabot.yml new file mode 100644 index 00000000..9d20742e --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + github-actions: + applies-to: version-updates + patterns: + - "*" diff --git a/api/vendor/maennchen/zipstream-php/.github/scorecard.yml b/api/vendor/maennchen/zipstream-php/.github/scorecard.yml new file mode 100644 index 00000000..219fc0bf --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/scorecard.yml @@ -0,0 +1,14 @@ +annotations: + - checks: + - fuzzing + reasons: + - reason: not-applicable # PHP is memory safe + - checks: + - packaging + reasons: + - reason: not-supported # Using Composer + - checks: + - signed-releases + reasons: + - reason: not-applicable # Releases are distributed via Composer + diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml new file mode 100644 index 00000000..15ff2782 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml @@ -0,0 +1,24 @@ +on: + push: + branches: + - "main" + +name: "Main Branch" + +permissions: + contents: read + +jobs: + test: + name: "Test" + + permissions: + contents: read + security-events: write + + uses: ./.github/workflows/part_test.yml + + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml new file mode 100644 index 00000000..20a13a20 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml @@ -0,0 +1,30 @@ +on: + workflow_call: {} + +name: "Dependabot" + +permissions: + contents: read + +jobs: + automerge_dependabot: + name: "Automerge PRs" + + runs-on: ubuntu-latest + + permissions: + pull-requests: write + contents: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - uses: fastify/github-action-merge-dependabot@c3bde0759d4f24db16f7b250b2122bc2df57e817 # v3.11.0 + with: + github-token: ${{ github.token }} + use-github-auto-merge: true + # Major Updates need to be merged manually + target: minor diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml new file mode 100644 index 00000000..9b779eb5 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml @@ -0,0 +1,51 @@ +on: + workflow_call: {} + +name: "Documentation" + +permissions: + contents: read + +jobs: + generate: + name: "Generate" + + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + tools: phive + - name: Cache Tools + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ~/.phive + key: tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-${{ hashFiles('**/phars.xml') }} + restore-keys: | + tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + tools-${{ steps.setup-php.outputs.php-version }}- + tools- + - name: Install Tools + run: composer run install:tools + - name: Generate Docs + run: composer run docs:generate + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: docs + path: docs + - name: Package for GitHub Pages + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + with: + path: docs + diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml new file mode 100644 index 00000000..112d72a4 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml @@ -0,0 +1,94 @@ +on: + workflow_call: + inputs: + releaseName: + required: true + type: string + stable: + required: false + type: boolean + default: false + +name: "Release" + +permissions: + contents: read + +jobs: + create: + name: Create Release + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Create prerelease + if: ${{ !inputs.stable }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create \ + --repo ${{ github.repository }} \ + --title ${{ inputs.releaseName }} \ + --prerelease \ + --generate-notes \ + ${{ inputs.releaseName }} + + - name: Create release + if: ${{ inputs.stable }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create \ + --repo ${{ github.repository }} \ + --title ${{ inputs.releaseName }} \ + --generate-notes \ + ${{ inputs.releaseName }} + + upload_release: + name: "Upload" + + needs: ["create"] + + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: write + attestations: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: docs + path: docs + - run: | + tar -czvf docs.tar.gz docs + - name: "Attest Documentation" + id: attestation + uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0 + with: + subject-path: "docs.tar.gz" + - name: Copy Attestation + run: cp "$ATTESTATION" docs.tar.gz.sigstore + env: + ATTESTATION: "${{ steps.attestation.outputs.bundle-path }}" + - name: Upload + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release upload --clobber "${{ github.ref_name }}" \ + docs.tar.gz docs.tar.gz.sigstore diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml new file mode 100644 index 00000000..d4f8180a --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml @@ -0,0 +1,181 @@ +on: + workflow_call: + +name: "Test" + +permissions: + contents: read + +jobs: + phpunit: + name: PHPUnit (PHP ${{ matrix.php }} on ${{ matrix.os }}) + + runs-on: ${{ matrix.os }} + + continue-on-error: ${{ matrix.experimental }} + + strategy: + fail-fast: false + matrix: + php: ["8.2", "8.3", "8.4"] + os: [ubuntu-latest] + experimental: [false] + include: + - php: nightly + os: ubuntu-latest + experimental: true + - php: "8.4" + os: windows-latest + experimental: false + - php: "8.4" + os: macos-latest + experimental: false + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "${{ matrix.php }}" + tools: phpunit + coverage: xdebug + extensions: xdebug,zip + - name: Get composer cache directory + id: composer-cache-common + if: "${{ runner.os != 'Windows' }}" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Get composer cache directory + id: composer-cache-windows + if: "${{ runner.os == 'Windows' }}" + run: echo "dir=$(composer config cache-files-dir)" >> $env:GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache-common.outputs.dir }}${{ steps.composer-cache-windows.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + if: matrix.php != 'nightly' + run: composer install --prefer-dist + - name: Install Deps (ignore PHP requirement) + if: matrix.php == 'nightly' + run: composer install --prefer-dist --ignore-platform-req=php+ + - name: Run PHPUnit + run: composer run test:unit:cov + - name: Upload coverage results to Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + COVERALLS_FLAG_NAME: ${{ runner.os }}-${{ steps.setup-php.outputs.php-version }} + run: composer run coverage:report + continue-on-error: ${{ matrix.experimental }} + + mark_coverage_done: + needs: ["phpunit"] + + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Coveralls Finished + uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true + + psalm: + name: Run Psalm + + runs-on: "ubuntu-latest" + + permissions: + security-events: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + run: composer install --prefer-dist + - name: Run Psalm + run: composer run test:lint -- --report=results.sarif + - name: "Upload SARIF" + uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3 + with: + sarif_file: results.sarif + + php-cs: + name: Run PHP-CS + + runs-on: "ubuntu-latest" + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + run: composer install --prefer-dist + - name: Run PHP-CS + run: composer run test:formatted diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/pr.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/pr.yml new file mode 100644 index 00000000..d21f3986 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/pr.yml @@ -0,0 +1,50 @@ +on: + pull_request: + branches: + - "*" + workflow_dispatch: {} + +name: "Pull Request" + +permissions: + contents: read + +jobs: + test: + name: "Test" + + permissions: + contents: read + security-events: write + + uses: ./.github/workflows/part_test.yml + + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + dependabot: + name: "Dependabot" + + if: ${{ github.actor == 'dependabot[bot]'}} + + permissions: + pull-requests: write + contents: write + + uses: ./.github/workflows/part_dependabot.yml + + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml new file mode 100644 index 00000000..c1d08a21 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml @@ -0,0 +1,78 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '28 11 * * 3' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5 + with: + sarif_file: results.sarif diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml new file mode 100644 index 00000000..b3399454 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml @@ -0,0 +1,29 @@ +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" + +name: "Beta Tag" + +permissions: + contents: read + +jobs: + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + release: + name: "Release" + + needs: ["docs"] + + permissions: + id-token: write + contents: write + attestations: write + + uses: ./.github/workflows/part_release.yml + with: + releaseName: "${{ github.ref_name }}" diff --git a/api/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml b/api/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml new file mode 100644 index 00000000..dfc14383 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml @@ -0,0 +1,55 @@ +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + +name: "Stable Tag" + +permissions: + contents: read + +jobs: + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + release: + name: "Release" + + needs: ["docs"] + + permissions: + id-token: write + contents: write + attestations: write + + uses: ./.github/workflows/part_release.yml + with: + releaseName: "${{ github.ref_name }}" + stable: true + + deploy_pages: + name: "Deploy to GitHub Pages" + + needs: ["release", "docs"] + + runs-on: ubuntu-latest + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/api/vendor/maennchen/zipstream-php/.gitignore b/api/vendor/maennchen/zipstream-php/.gitignore new file mode 100644 index 00000000..e52a4987 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.gitignore @@ -0,0 +1,12 @@ +/composer.lock +/cov +/coverage.clover.xml +/docs +.idea +/.php-cs-fixer.cache +/.phpdoc/cache +/.phpunit.result.cache +/phpunit.xml +/.phpunit.cache +/tools +/vendor diff --git a/api/vendor/maennchen/zipstream-php/.phive/phars.xml b/api/vendor/maennchen/zipstream-php/.phive/phars.xml new file mode 100644 index 00000000..c958402b --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.phive/phars.xml @@ -0,0 +1,4 @@ + + + + diff --git a/api/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php b/api/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php new file mode 100644 index 00000000..9d47c384 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php @@ -0,0 +1,73 @@ + + * @copyright 2022 Nicolas CARPi + * @see https://github.com/maennchen/ZipStream-PHP + * @license MIT + * @package maennchen/ZipStream-PHP + */ + +use PhpCsFixer\Config; +use PhpCsFixer\Finder; +use PhpCsFixer\Runner; + +$finder = Finder::create() + ->exclude('.github') + ->exclude('.phpdoc') + ->exclude('docs') + ->exclude('tools') + ->exclude('vendor') + ->in(__DIR__); + +$config = new Config(); +return $config->setRules([ + '@PER' => true, + '@PER:risky' => true, + '@PHP83Migration' => true, + '@PHP84Migration' => true, + '@PHPUnit84Migration:risky' => true, + 'array_syntax' => ['syntax' => 'short'], + 'class_attributes_separation' => true, + 'declare_strict_types' => true, + 'dir_constant' => true, + 'is_null' => true, + 'no_homoglyph_names' => true, + 'no_null_property_initialization' => true, + 'no_php4_constructor' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'non_printable_character' => true, + 'ordered_imports' => true, + 'ordered_class_elements' => true, + 'php_unit_construct' => true, + 'pow_to_exponentiation' => true, + 'psr_autoloading' => true, + 'random_api_migration' => true, + 'return_assignment' => true, + 'self_accessor' => true, + 'semicolon_after_instruction' => true, + 'short_scalar_cast' => true, + 'simplified_null_return' => true, + 'single_class_element_per_statement' => true, + 'single_line_comment_style' => true, + 'single_quote' => true, + 'space_after_semicolon' => true, + 'standardize_not_equals' => true, + 'strict_param' => true, + 'ternary_operator_spaces' => true, + 'trailing_comma_in_multiline' => true, + 'trim_array_spaces' => true, + 'unary_operator_spaces' => true, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_functions' => true, + 'import_constants' => true, + ], + ]) + ->setFinder($finder) + ->setRiskyAllowed(true) + ->setParallelConfig(Runner\Parallel\ParallelConfigFactory::detect()); diff --git a/api/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig b/api/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig new file mode 100644 index 00000000..2a70c0aa --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig @@ -0,0 +1,15 @@ +{% extends 'layout.html.twig' %} + +{% set topMenu = { + "menu": [ + { "name": "Guides", "url": "https://maennchen.dev/ZipStream-PHP/guide/index.html"}, + { "name": "API", "url": "https://maennchen.dev/ZipStream-PHP/classes/ZipStream-ZipStream.html"}, + { "name": "Issues", "url": "https://github.com/maennchen/ZipStream-PHP/issues"}, + ], + "social": [ + { "iconClass": "fab fa-github", "url": "https://github.com/maennchen/ZipStream-PHP"}, + { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/maennchen/ZipStream-PHP/discussions"}, + { "iconClass": "fas fa-money-bill", "url": "https://github.com/sponsors/maennchen"}, + ] +} +%} \ No newline at end of file diff --git a/api/vendor/maennchen/zipstream-php/.tool-versions b/api/vendor/maennchen/zipstream-php/.tool-versions new file mode 100644 index 00000000..150c1ee4 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/.tool-versions @@ -0,0 +1 @@ +php 8.4.3 diff --git a/api/vendor/maennchen/zipstream-php/LICENSE b/api/vendor/maennchen/zipstream-php/LICENSE new file mode 100644 index 00000000..ebe7fe2f --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (C) 2007-2009 Paul Duncan +Copyright (C) 2014 Jonatan Männchen +Copyright (C) 2014 Jesse G. Donat +Copyright (C) 2018 Nicolas CARPi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/api/vendor/maennchen/zipstream-php/README.md b/api/vendor/maennchen/zipstream-php/README.md new file mode 100644 index 00000000..1e6d6798 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/README.md @@ -0,0 +1,157 @@ +# ZipStream-PHP + +[![Main Branch](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml/badge.svg)](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml) +[![Coverage Status](https://coveralls.io/repos/github/maennchen/ZipStream-PHP/badge.svg?branch=main)](https://coveralls.io/github/maennchen/ZipStream-PHP?branch=main) +[![Latest Stable Version](https://poser.pugx.org/maennchen/zipstream-php/v/stable)](https://packagist.org/packages/maennchen/zipstream-php) +[![Total Downloads](https://poser.pugx.org/maennchen/zipstream-php/downloads)](https://packagist.org/packages/maennchen/zipstream-php) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9524/badge)](https://www.bestpractices.dev/projects/9524) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/maennchen/ZipStream-PHP/badge)](https://scorecard.dev/viewer/?uri=github.com/maennchen/ZipStream-PHP) + +## Unstable Branch + +The `main` branch is not stable. Please see the +[releases](https://github.com/maennchen/ZipStream-PHP/releases) for a stable +version. + +## Overview + +A fast and simple streaming zip file downloader for PHP. Using this library will +save you from having to write the Zip to disk. You can directly send it to the +user, which is much faster. It can work with S3 buckets or any PSR7 Stream. + +Please see the [LICENSE](LICENSE) file for licensing and warranty information. + +## Installation + +Simply add a dependency on maennchen/zipstream-php to your project's +`composer.json` file if you use Composer to manage the dependencies of your +project. Use following command to add the package to your project's dependencies: + +```bash +composer require maennchen/zipstream-php +``` + +## Usage + +For detailed instructions, please check the +[Documentation](https://maennchen.github.io/ZipStream-PHP/). + +```php +// Autoload the dependencies +require 'vendor/autoload.php'; + +// create a new zipstream object +$zip = new ZipStream\ZipStream( + outputName: 'example.zip', + + // enable output of HTTP headers + sendHttpHeaders: true, +); + +// create a file named 'hello.txt' +$zip->addFile( + fileName: 'hello.txt', + data: 'This is the contents of hello.txt', +); + +// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' +$zip->addFileFromPath( + fileName: 'some_image.jpg', + path: 'path/to/image.jpg', +); + +// finish the zip stream +$zip->finish(); +``` + +## Upgrade to version 3.1.2 + +- Minimum PHP Version: `8.2` + +## Upgrade to version 3.0.0 + +### General + +- Minimum PHP Version: `8.1` +- Only 64bit Architecture is supported. +- The class `ZipStream\Option\Method` has been replaced with the enum + `ZipStream\CompressionMethod`. +- Most clases have been flagged as `@internal` and should not be used from the + outside. + If you're using internal resources to extend this library, please open an + issue so that a clean interface can be added & published. + The externally available classes & enums are: + - `ZipStream\CompressionMethod` + - `ZipStream\Exception*` + - `ZipStream\ZipStream` + +### Archive Options + +- The class `ZipStream\Option\Archive` has been replaced in favor of named + arguments in the `ZipStream\ZipStream` constuctor. +- The archive options `largeFileSize` & `largeFileMethod` has been removed. If + you want different `compressionMethods` based on the file size, you'll have to + implement this yourself. +- The archive option `httpHeaderCallback` changed the type from `callable` to + `Closure`. +- The archive option `zeroHeader` has been replaced with the option + `defaultEnableZeroHeader` and can be overridden for every file. Its default + value changed from `false` to `true`. +- The archive option `statFiles` was removed since the library no longer checks + filesizes this way. +- The archive option `deflateLevel` has been replaced with the option + `defaultDeflateLevel` and can be overridden for every file. +- The first argument (`name`) of the `ZipStream\ZipStream` constuctor has been + replaced with the named argument `outputName`. +- Headers are now also sent if the `outputName` is empty. If you do not want to + automatically send http headers, set `sendHttpHeaders` to `false`. + +### File Options + +- The class `ZipStream\Option\File` has been replaced in favor of named + arguments in the `ZipStream\ZipStream->addFile*` functions. +- The file option `method` has been renamed to `compressionMethod`. +- The file option `time` has been renamed to `lastModificationDateTime`. +- The file option `size` has been renamed to `maxSize`. + +## Upgrade to version 2.0.0 + +https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-200 + +## Upgrade to version 1.0.0 + +https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-100 + +## Contributing + +ZipStream-PHP is a collaborative project. Please take a look at the +[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file. + +## Version Support + +Versions are supported according to the table below. + +Please do not open any pull requests contradicting the current version support +status. + +Careful: Always check the `README` on `main` for up-to-date information. + +| Version | New Features | Bugfixes | Security | +|---------|--------------|----------|----------| +| *3* | ✓ | ✓ | ✓ | +| *2* | ✗ | ✗ | ✓ | +| *1* | ✗ | ✗ | ✗ | +| *0* | ✗ | ✗ | ✗ | + +This library aligns itself with the PHP core support. New features and bugfixes +will only target PHP versions according to their current status. + +See: https://www.php.net/supported-versions.php + +## About the Authors + +- Paul Duncan - https://pablotron.org/ +- Jonatan Männchen - https://maennchen.dev +- Jesse G. Donat - https://donatstudios.com +- Nicolas CARPi - https://www.deltablot.com +- Nik Barham - https://www.brokencube.co.uk diff --git a/api/vendor/maennchen/zipstream-php/composer.json b/api/vendor/maennchen/zipstream-php/composer.json new file mode 100644 index 00000000..6ecd503a --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/composer.json @@ -0,0 +1,93 @@ +{ + "name": "maennchen/zipstream-php", + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": ["zip", "stream"], + "type": "library", + "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" + } + ], + "require": { + "php-64bit": "^8.2", + "ext-mbstring": "*", + "ext-zlib": "*" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "guzzlehttp/guzzle": "^7.5", + "ext-zip": "*", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "friendsofphp/php-cs-fixer": "^3.16", + "vimeo/psalm": "^6.0", + "brianium/paratest": "^7.7" + }, + "suggest": { + "psr/http-message": "^2.0", + "guzzlehttp/psr7": "^2.4" + }, + "scripts": { + "format": "php-cs-fixer fix", + "test": [ + "@test:unit", + "@test:formatted", + "@test:lint" + ], + "test:unit:setup-cov": "@putenv XDEBUG_MODE=coverage", + "test:unit": "paratest --functional", + "test:unit:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov"], + "test:unit:slow": "@test:unit --group slow", + "test:unit:slow:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --group slow"], + "test:unit:fast": "@test:unit --exclude-group slow", + "test:unit:fast:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --exclude-group slow"], + "test:formatted": "@format --dry-run --stop-on-violation --using-cache=no", + "test:lint": "psalm --stats --show-info=true --find-unused-psalm-suppress", + "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure", + "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656 --trust-gpg-keys 0x8AC0BAA79732DD42", + "docs:generate": "tools/phpdocumentor --sourcecode" + }, + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { "ZipStream\\Test\\": "test/" } + }, + "archive": { + "exclude": [ + "/composer.lock", + "/docs", + "/.gitattributes", + "/.github", + "/.gitignore", + "/guides", + "/.phive", + "/.php-cs-fixer.cache", + "/.php-cs-fixer.dist.php", + "/.phpdoc", + "/phpdoc.dist.xml", + "/.phpunit.result.cache", + "/phpunit.xml.dist", + "/psalm.xml", + "/test", + "/tools", + "/.tool-versions", + "/vendor" + ] + } +} diff --git a/api/vendor/maennchen/zipstream-php/guides/ContentLength.rst b/api/vendor/maennchen/zipstream-php/guides/ContentLength.rst new file mode 100644 index 00000000..21fea34d --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/ContentLength.rst @@ -0,0 +1,47 @@ +Adding Content-Length header +============= + +Adding a ``Content-Length`` header for ``ZipStream`` can be achieved by +using the options ``SIMULATION_STRICT`` or ``SIMULATION_LAX`` in the +``operationMode`` parameter. + +In the ``SIMULATION_STRICT`` mode, ``ZipStream`` will not allow to calculate the +size based on reading the whole file. ``SIMULATION_LAX`` will read the whole +file if neccessary. + +``SIMULATION_STRICT`` is therefore useful to make sure that the size can be +calculated efficiently. + +.. code-block:: php + use ZipStream\OperationMode; + use ZipStream\ZipStream; + + $zip = new ZipStream( + operationMode: OperationMode::SIMULATE_STRICT, // or SIMULATE_LAX + defaultEnableZeroHeader: false, + sendHttpHeaders: true, + outputStream: $stream, + ); + + // Normally add files + $zip->addFile('sample.txt', 'Sample String Data'); + + // Use addFileFromCallback and exactSize if you want to defer opening of + // the file resource + $zip->addFileFromCallback( + 'sample.txt', + exactSize: 18, + callback: function () { + return fopen('...'); + } + ); + + // Read resulting file size + $size = $zip->finish(); + + // Tell it to the browser + header('Content-Length: '. $size); + + // Execute the Simulation and stream the actual zip to the client + $zip->executeSimulation(); + diff --git a/api/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/api/vendor/maennchen/zipstream-php/guides/FlySystem.rst new file mode 100644 index 00000000..4e6c6fb8 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/FlySystem.rst @@ -0,0 +1,34 @@ +Usage with FlySystem +=============== + +For saving or uploading the generated zip, you can use the +`Flysystem `_ package, and its many +adapters. + +For that you will need to provide another stream than the ``php://output`` +default one, and pass it to Flysystem ``putStream`` method. + +.. code-block:: php + + // Open Stream only once for read and write since it's a memory stream and + // the content is lost when closing the stream / opening another one + $tempStream = fopen('php://memory', 'w+'); + + // Create Zip Archive + $zipStream = new ZipStream( + outputStream: $tempStream, + outputName: 'test.zip', + ); + $zipStream->addFile('test.txt', 'text'); + $zipStream->finish(); + + // Store File + // (see Flysystem documentation, and all its framework integration) + // Can be any adapter (AWS, Google, Ftp, etc.) + $adapter = new Local(__DIR__.'/path/to/folder'); + $filesystem = new Filesystem($adapter); + + $filesystem->writeStream('test.zip', $tempStream) + + // Close Stream + fclose($tempStream); diff --git a/api/vendor/maennchen/zipstream-php/guides/Nginx.rst b/api/vendor/maennchen/zipstream-php/guides/Nginx.rst new file mode 100644 index 00000000..c53d3000 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/Nginx.rst @@ -0,0 +1,16 @@ +Usage with nginx +============= + +If you are using nginx as a webserver, it will try to buffer the response. +So you'll want to disable this with a custom header: + +.. code-block:: php + header('X-Accel-Buffering: no'); + # or with the Response class from Symfony + $response->headers->set('X-Accel-Buffering', 'no'); + +Alternatively, you can tweak the +`fastcgi cache parameters `_ +within nginx config. + +See `original issue `_. \ No newline at end of file diff --git a/api/vendor/maennchen/zipstream-php/guides/Options.rst b/api/vendor/maennchen/zipstream-php/guides/Options.rst new file mode 100644 index 00000000..5e92e94d --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/Options.rst @@ -0,0 +1,66 @@ +Available options +=============== + +Here is the full list of options available to you. You can also have a look at +``src/ZipStream.php`` file. + +.. code-block:: php + + use ZipStream\ZipStream; + + require_once 'vendor/autoload.php'; + + $zip = new ZipStream( + // Define output stream + // (argument is eiter a resource or implementing + // `Psr\Http\Message\StreamInterface`) + // + // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies + // required when using `Psr\Http\Message\StreamInterface`. + outputStream: $filePointer, + + // Set the deflate level (default is 6; use -1 to disable it) + defaultDeflateLevel: 6, + + // Add a comment to the zip file + comment: 'This is a comment.', + + // Send http headers (default is true) + sendHttpHeaders: false, + + // HTTP Content-Disposition. + // Defaults to 'attachment', where FILENAME is the specified filename. + // Note that this does nothing if you are not sending HTTP headers. + contentDisposition: 'attachment', + + // Output Name for HTTP Content-Disposition + // Defaults to no name + outputName: "example.zip", + + // HTTP Content-Type. + // Defaults to 'application/x-zip'. + // Note that this does nothing if you are not sending HTTP headers. + contentType: 'application/x-zip', + + // Set the function called for setting headers. + // Default is the `header()` of PHP + httpHeaderCallback: header(...), + + // Enable streaming files with single read where general purpose bit 3 + // indicates local file header contain zero values in crc and size + // fields, these appear only after file contents in data descriptor + // block. + // Set to true if your input stream is remote + // (used with addFileFromStream()). + // Default is false. + defaultEnableZeroHeader: false, + + // Enable zip64 extension, allowing very large archives + // (> 4Gb or file count > 64k) + // Default is true + enableZip64: true, + + // Flush output buffer after every write + // Default is false + flushOutput: true, + ); diff --git a/api/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/api/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst new file mode 100644 index 00000000..22af71d4 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst @@ -0,0 +1,21 @@ +Usage with PSR 7 Streams +=============== + +PSR-7 streams are `standardized streams `_. + +ZipStream-PHP supports working with these streams with the function +``addFileFromPsr7Stream``. + +For all parameters of the function see the API documentation. + +Example +--------------- + +.. code-block:: php + + $stream = $response->getBody(); + // add a file named 'streamfile.txt' from the content of the stream + $zip->addFileFromPsr7Stream( + fileName: 'streamfile.txt', + stream: $stream, + ); diff --git a/api/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/api/vendor/maennchen/zipstream-php/guides/StreamOutput.rst new file mode 100644 index 00000000..9f3165b7 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/StreamOutput.rst @@ -0,0 +1,39 @@ +Stream Output +=============== + +Stream to S3 Bucket +--------------- + +.. code-block:: php + + use Aws\S3\S3Client; + use Aws\Credentials\CredentialProvider; + use ZipStream\ZipStream; + + $bucket = 'your bucket name'; + $client = new S3Client([ + 'region' => 'your region', + 'version' => 'latest', + 'bucketName' => $bucket, + 'credentials' => CredentialProvider::defaultProvider(), + ]); + $client->registerStreamWrapper(); + + $zipFile = fopen("s3://$bucket/example.zip", 'w'); + + $zip = new ZipStream( + enableZip64: false, + outputStream: $zipFile, + ); + + $zip->addFile( + fileName: 'file1.txt', + data: 'File1 data', + ); + $zip->addFile( + fileName: 'file2.txt', + data: 'File2 data', + ); + $zip->finish(); + + fclose($zipFile); diff --git a/api/vendor/maennchen/zipstream-php/guides/Symfony.rst b/api/vendor/maennchen/zipstream-php/guides/Symfony.rst new file mode 100644 index 00000000..902552c9 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/Symfony.rst @@ -0,0 +1,130 @@ +Usage with Symfony +=============== + +Overview for using ZipStream in Symfony +-------- + +Using ZipStream in Symfony requires use of Symfony's ``StreamedResponse`` when +used in controller actions. + +Wrap your call to the relevant ``ZipStream`` stream method (i.e. ``addFile``, +``addFileFromPath``, ``addFileFromStream``) in Symfony's ``StreamedResponse`` +function passing in any required arguments for your use case. + +Using Symfony's ``StreamedResponse`` will allow Symfony to stream output from +ZipStream correctly to users' browsers and avoid a corrupted final zip landing +on the users' end. + +Example for using ``ZipStream`` in a controller action to zip stream files +stored in an AWS S3 bucket by key: + +.. code-block:: php + + use Symfony\Component\HttpFoundation\StreamedResponse; + use Aws\S3\S3Client; + use ZipStream; + + //... + + /** + * @Route("/zipstream", name="zipstream") + */ + public function zipStreamAction() + { + // sample test file on s3 + $s3keys = array( + "ziptestfolder/file1.txt" + ); + + $s3Client = $this->get('app.amazon.s3'); //s3client service + $s3Client->registerStreamWrapper(); //required + + // using StreamedResponse to wrap ZipStream functionality + // for files on AWS s3. + $response = new StreamedResponse(function() use($s3keys, $s3Client) + { + // Define suitable options for ZipStream Archive. + // this is needed to prevent issues with truncated zip files + //initialise zipstream with output zip filename and options. + $zip = new ZipStream\ZipStream( + outputName: 'test.zip', + defaultEnableZeroHeader: true, + contentType: 'application/octet-stream', + ); + + //loop keys - useful for multiple files + foreach ($s3keys as $key) { + // Get the file name in S3 key so we can save it to the zip + //file using the same name. + $fileName = basename($key); + + // concatenate s3path. + // replace with your bucket name or get from parameters file. + $bucket = 'bucketname'; + $s3path = "s3://" . $bucket . "/" . $key; + + //addFileFromStream + if ($streamRead = fopen($s3path, 'r')) { + $zip->addFileFromStream( + fileName: $fileName, + stream: $streamRead, + ); + } else { + die('Could not open stream for reading'); + } + } + + $zip->finish(); + + }); + + return $response; + } + +In the above example, files on AWS S3 are being streamed from S3 to the Symfon +application via ``fopen`` call when the s3Client has ``registerStreamWrapper`` +applied. This stream is then passed to ``ZipStream`` via the +``addFileFromStream`` function, which ZipStream then streams as a zip to the +client browser via Symfony's ``StreamedResponse``. No Zip is created server +side, which makes this approach a more efficient solution for streaming zips to +the client browser especially for larger files. + +For the above use case you will need to have installed +`aws/aws-sdk-php-symfony `_ to +support accessing S3 objects in your Symfony web application. This is not +required for locally stored files on you server you intend to stream via +``ZipStream``. + +See official Symfony documentation for details on +`Symfony's StreamedResponse `_ +``Symfony\Component\HttpFoundation\StreamedResponse``. + +Note from `S3 documentation `_: + + Streams opened in "r" mode only allow data to be read from the stream, and + are not seekable by default. This is so that data can be downloaded from + Amazon S3 in a truly streaming manner, where previously read bytes do not + need to be buffered into memory. If you need a stream to be seekable, you + can pass seekable into the stream context options of a function. + +Make sure to configure your S3 context correctly! + +Uploading a file +-------- + +You need to add correct permissions +(see `#120 `_) + +**example code** + + +.. code-block:: php + + $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}"; + + // the important bit + $outputContext = stream_context_create([ + 's3' => ['ACL' => 'public-read'], + ]); + + fopen($path, 'w', null, $outputContext); diff --git a/api/vendor/maennchen/zipstream-php/guides/Varnish.rst b/api/vendor/maennchen/zipstream-php/guides/Varnish.rst new file mode 100644 index 00000000..952d2874 --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/Varnish.rst @@ -0,0 +1,22 @@ +Usage with Varnish +============= + +Serving a big zip with varnish in between can cause random stream close. +This can be solved by adding attached code to the vcl file. + +To avoid the problem, add the following to your varnish config file: + +.. code-block:: + sub vcl_recv { + # Varnish can’t intercept the discussion anymore + # helps for streaming big zips + if (req.url ~ "\.(tar|gz|zip|7z|exe)$") { + return (pipe); + } + } + # Varnish can’t intercept the discussion anymore + # helps for streaming big zips + sub vcl_pipe { + set bereq.http.connection = "close"; + return (pipe); + } diff --git a/api/vendor/maennchen/zipstream-php/guides/index.rst b/api/vendor/maennchen/zipstream-php/guides/index.rst new file mode 100644 index 00000000..48f465ae --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/guides/index.rst @@ -0,0 +1,126 @@ +ZipStream PHP +============= + +A fast and simple streaming zip file downloader for PHP. Using this library will +save you from having to write the Zip to disk. You can directly send it to the +user, which is much faster. It can work with S3 buckets or any PSR7 Stream. + +.. toctree:: + + index + Symfony + Options + StreamOutput + FlySystem + PSR7Streams + Nginx + Varnish + ContentLength + +Installation +--------------- + +Simply add a dependency on ``maennchen/zipstream-php`` to your project's +``composer.json`` file if you use Composer to manage the dependencies of your +project. Use following command to add the package to your project's +dependencies: + +.. code-block:: sh + composer require maennchen/zipstream-php + +If you want to use``addFileFromPsr7Stream``` +(``Psr\Http\Message\StreamInterface``) or use a stream instead of a +``resource`` as ``outputStream``, the following dependencies must be installed +as well: + +.. code-block:: sh + composer require psr/http-message guzzlehttp/psr7 + +If ``composer install`` yields the following error, your installation is missing +the `mbstring extension `_, +either `install it `_ +or run the follwoing command: + +.. code-block:: + Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - Root composer.json requires PHP extension ext-mbstring * but it is + missing from your system. Install or enable PHP's mbstrings extension. + +.. code-block:: sh + composer require symfony/polyfill-mbstring + +Usage Intro +--------------- + +Here's a simple example: + +.. code-block:: php + + // Autoload the dependencies + require 'vendor/autoload.php'; + + // create a new zipstream object + $zip = new ZipStream\ZipStream( + outputName: 'example.zip', + + // enable output of HTTP headers + sendHttpHeaders: true, + ); + + // create a file named 'hello.txt' + $zip->addFile( + fileName: 'hello.txt', + data: 'This is the contents of hello.txt', + ); + + // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' + $zip->addFileFromPath( + fileName: 'some_image.jpg', + path: 'path/to/image.jpg', + ); + + // add a file named 'goodbye.txt' from an open stream resource + $filePointer = tmpfile(); + fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.'); + rewind($filePointer); + $zip->addFileFromStream( + fileName: 'goodbye.txt', + stream: $filePointer, + ); + fclose($filePointer); + + // add a file named 'streamfile.txt' from the body of a `guzzle` response + // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies required. + $zip->addFileFromPsr7Stream( + fileName: 'streamfile.txt', + stream: $response->getBody(), + ); + + // finish the zip stream + $zip->finish(); + +You can also add comments, modify file timestamps, and customize (or +disable) the HTTP headers. It is also possible to specify the storage method +when adding files, the current default storage method is ``DEFLATE`` +i.e files are stored with Compression mode 0x08. + +Known Issues +--------------- + +The native Mac OS archive extraction tool prior to macOS 10.15 might not open +archives in some conditions. A workaround is to disable the Zip64 feature with +the option ``enableZip64: false``. This limits the archive to 4 Gb and 64k files +but will allow users on macOS 10.14 and below to open them without issue. +See `#116 `_. + +The linux ``unzip`` utility might not handle properly unicode characters. +It is recommended to extract with another tool like +`7-zip `_. +See `#146 `_. + +It is the responsability of the client code to make sure that files are not +saved with the same path, as it is not possible for the library to figure it out +while streaming a zip. +See `#154 `_. diff --git a/api/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/api/vendor/maennchen/zipstream-php/phpdoc.dist.xml new file mode 100644 index 00000000..b98fe1cd --- /dev/null +++ b/api/vendor/maennchen/zipstream-php/phpdoc.dist.xml @@ -0,0 +1,39 @@ + + + 💾 ZipStream-PHP + + docs + + + latest + + + src + + api + + + php + + public + ZipStream + true + + + + guides + + guide + + + +