From a9ae78426764236d531bb0c4963f28362ad30623 Mon Sep 17 00:00:00 2001 From: MarijusDilys Date: Wed, 19 Nov 2025 10:36:28 +0200 Subject: [PATCH 01/28] Fix double discount calculation in COD label goods price Issue: When discount codes are applied, the COD label shows incorrect goods price because discount is subtracted twice from order total. Root cause: createNonDistributedShipment method was subtracting order discount from order.total_paid, which already includes all discounts. Solution: Removed duplicate discount calculation in createNonDistributedShipment method. The order.total_paid value correctly reflects the final amount customer pays. Also fixed syntax error on line 283 (removed stray character). DGS-410 --- src/Service/ShipmentService.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service/ShipmentService.php b/src/Service/ShipmentService.php index 1a1f2e8e..5f1636bc 100644 --- a/src/Service/ShipmentService.php +++ b/src/Service/ShipmentService.php @@ -232,7 +232,6 @@ private function createNonDistributedShipment(Order $order, $idProduct, $isTestM $parcelWeight += $product['weight'] * $product['product_quantity']; } - $goodsPrice = $this->calculateParcelPriceWithOrderDiscount($order, $goodsPrice); $shipment = $this->createShipment($order, $idProduct, $isTestMode, 1, $parcelWeight, $goodsPrice); if (!$shipment->id) { From f078d35584436401a01e6baca7941be2efcb410a Mon Sep 17 00:00:00 2001 From: MarijusDilys Date: Wed, 19 Nov 2025 11:10:00 +0200 Subject: [PATCH 02/28] Bump version to 3.3.1 and update changelog --- CHANGELOG.md | 4 ++++ dpdbaltics.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a49027..24b9cc85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -182,6 +182,10 @@ - Added warning when module have outdated version - Improved module performance +## [3.3.1] - 2025-11-19 +### Fixed +- Fixed incorrect COD label amount when discount code is applied + ## [3.3.0] - Added PrestaShop 9 compatibility - Fixed issue with price rule "All" diff --git a/dpdbaltics.php b/dpdbaltics.php index c2bf51c3..7a1d2241 100644 --- a/dpdbaltics.php +++ b/dpdbaltics.php @@ -85,7 +85,7 @@ public function __construct() $this->author = 'Invertus'; $this->tab = 'shipping_logistics'; $this->description = 'DPD Baltics shipping integration'; - $this->version = '3.3.0'; + $this->version = '3.3.1'; $this->ps_versions_compliancy = ['min' => '1.7.1.0', 'max' => _PS_VERSION_]; $this->need_instance = 0; parent::__construct(); From 24ef0a0c53b7a11c7462d038d72b61d8ab6c27d8 Mon Sep 17 00:00:00 2001 From: MarijusDilys Date: Wed, 19 Nov 2025 11:11:01 +0200 Subject: [PATCH 03/28] fix --- CHANGELOG.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b9cc85..f51747b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -182,11 +182,10 @@ - Added warning when module have outdated version - Improved module performance -## [3.3.1] - 2025-11-19 -### Fixed -- Fixed incorrect COD label amount when discount code is applied - ## [3.3.0] - Added PrestaShop 9 compatibility - Fixed issue with price rule "All" -- Fixed other minor issues \ No newline at end of file +- Fixed other minor issues + +## [3.3.1] +- Fixed incorrect COD label amount when discount code is applied \ No newline at end of file From 6e700d1d39d93876870889e72201ded9cc38b87a Mon Sep 17 00:00:00 2001 From: TLabutis Date: Tue, 24 Mar 2026 16:25:07 +0200 Subject: [PATCH 04/28] DGS-415 fix null parameter deprecation in AddressAdapter preg_replace calls (#157) Add null coalescing operator to prevent PHP 8.1 deprecation warning when null is passed to preg_replace() in AddressAdapter.php on lines 91, 123, 156. Co-authored-by: Tadas Labutis --- src/Adapter/AddressAdapter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Adapter/AddressAdapter.php b/src/Adapter/AddressAdapter.php index c951fa24..91246911 100644 --- a/src/Adapter/AddressAdapter.php +++ b/src/Adapter/AddressAdapter.php @@ -88,7 +88,7 @@ private function getFormattedZipCode(Country $country, $postCode) { $countryCodePosition = $this->getCountryCodePosition($country); - $postCode = preg_replace("/[^a-zA-Z0-9]+/", "", $postCode); + $postCode = preg_replace("/[^a-zA-Z0-9]+/", "", $postCode ?? ''); // If C doesn't exist in zip code format - don't modify the zip code if (false === $countryCodePosition) { return $postCode; @@ -120,7 +120,7 @@ private function getFormattedZipCode(Country $country, $postCode) /** Changes zip code format from pudo service to the one used in prestashop as based on country and returns it*/ public function getFormattedZipCodePudoToPrestashop($iso, $zipCode) { - $zipCode = preg_replace("/[^a-zA-Z0-9]+/", "", $zipCode); + $zipCode = preg_replace("/[^a-zA-Z0-9]+/", "", $zipCode ?? ''); $country = new Country(Country::getByIso($iso)); $formattedZipCode = $zipCode; $isoAdded = false; @@ -153,6 +153,6 @@ public function formatPostCodeByCountry($postCode, $countryIsoCode) return str_replace(' ', '', $postCode); } - return preg_replace('/[^0-9]/', '', $postCode); + return preg_replace('/[^0-9]/', '', $postCode ?? ''); } } From 71532f95bd03f3f8768be3fb44ca428e96b0e504 Mon Sep 17 00:00:00 2001 From: TLabutis Date: Tue, 24 Mar 2026 16:25:36 +0200 Subject: [PATCH 05/28] DGS-417 Fix shipment creation failing when Predict SMS service is mandatory (#158) Enable DPD Predict SMS service by setting predict=y on all shipment creation requests. Some DPD client accounts (notably Latvia) require this parameter, causing shipment creation to fail without it. Co-authored-by: Tadas Labutis --- CHANGELOG.md | 3 +++ src/Service/API/ShipmentApiService.php | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f51747b8..8dad889f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -182,6 +182,9 @@ - Added warning when module have outdated version - Improved module performance +## [3.3.1] +- Fixed shipment creation failing for accounts requiring Predict SMS service + ## [3.3.0] - Added PrestaShop 9 compatibility - Fixed issue with price rule "All" diff --git a/src/Service/API/ShipmentApiService.php b/src/Service/API/ShipmentApiService.php index c3f07e3b..21cfa714 100644 --- a/src/Service/API/ShipmentApiService.php +++ b/src/Service/API/ShipmentApiService.php @@ -234,6 +234,7 @@ private function setNotRequiredData(ShipmentCreationRequest $shipmentCreationReq $shipmentCreationRequest->setOrderNumber3($shipmentData->getReference4()); $shipmentCreationRequest->setWeight($shipmentData->getWeight()); $shipmentCreationRequest->setIdmSmsNumber($shipmentData->getPhone()); + $shipmentCreationRequest->setPredict('y'); $shipmentCreationRequest->setOrderNumber($shipmentData->getReference1()); return $shipmentCreationRequest; From e12f252f630ab92590d064966bf7eb23c9ab1872 Mon Sep 17 00:00:00 2001 From: Gytautas Zumaras <96050852+GytisZum@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:07:04 +0200 Subject: [PATCH 06/28] DGS-414: update parcel import functionality to handle large quanities (#155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update large parcel update logic and cron job to ensure it will not time out * fix Poland parcel shops and various null pointer errors (#156) * fix Poland parcel shops and various null pointer errors - Add logger argument to ParcelTrackingEmailHandler service definitions - Fix null pointer errors when accessing parcel shop arrays - Fix postcode formatting for PUDO return shipments (strip hyphens) - Add null checks for selectedPudo in admin template - Change frontend message from "No pickup points found" to "Select a city to view pickup points" - Update console command to lazy-load services from module container - Register console command with PrestaShop's Symfony container Co-Authored-By: Claude Opus 4.5 * fix dpdbaltics JS variable undefined on standard checkout The dpdbaltics JS variable was only defined inside the OPC module conditional block, causing ReferenceError on standard checkout when pudo.js references dpdbaltics.isOnePageCheckout. This prevented PUDO pickup point selection from working on any PS version without an OPC module installed. --------- Co-authored-by: Tadas Labutis Co-authored-by: Claude Opus 4.5 Co-authored-by: Tadas Labutis * DGS-414 Fix pudo display and save data overwrite in backoffice order view (#160) * fix Poland parcel shops and various null pointer errors - Add logger argument to ParcelTrackingEmailHandler service definitions - Fix null pointer errors when accessing parcel shop arrays - Fix postcode formatting for PUDO return shipments (strip hyphens) - Add null checks for selectedPudo in admin template - Change frontend message from "No pickup points found" to "Select a city to view pickup points" - Update console command to lazy-load services from module container - Register console command with PrestaShop's Symfony container Co-Authored-By: Claude Opus 4.5 * fix dpdbaltics JS variable undefined on standard checkout The dpdbaltics JS variable was only defined inside the OPC module conditional block, causing ReferenceError on standard checkout when pudo.js references dpdbaltics.isOnePageCheckout. This prevented PUDO pickup point selection from working on any PS version without an OPC module installed. * DGS-414 Fix pudo display bug and save data overwrite - Fix backoffice order view not showing parcelshop info when customer city differs from parcelshop city by looking up shop by pudo_id first instead of gating it behind city-based search results - Fix savePudoOrder overwriting correct parcelshop city/street/postcode with customer address data by using dpd_shop data as source of truth - Fix Lithuanian translation "laivyba" -> "siuntimas" - Fix Latvian translation "kuģniecība" -> "piegāde" * DGS-414 Add changelog entry for v3.3.1 --------- Co-authored-by: Tadas Labutis Co-authored-by: Claude Opus 4.5 Co-authored-by: Tadas Labutis Co-authored-by: Tadas Labutis Co-authored-by: Gytautas Zumaras <96050852+GytisZum@users.noreply.github.com> --------- Co-authored-by: Gytautas Zumaras Co-authored-by: TLabutis Co-authored-by: Tadas Labutis Co-authored-by: Claude Opus 4.5 Co-authored-by: Tadas Labutis Co-authored-by: Tadas Labutis --- CHANGELOG.md | 6 + composer.lock | 4011 ----------------- config/command.yml | 5 +- config/requestFactory.yml | 2 +- config/service.yml | 6 +- config/services.yml | 10 + .../admin/AdminDPDBalticsAjaxController.php | 67 +- .../AdminDPDBalticsImportExportController.php | 11 +- controllers/front/CronJob.php | 34 +- dpdbaltics.php | 19 +- src/Config/Config.php | 2 + .../UpdateParcelShopsCommand.php | 237 + src/ConsoleCommand/index.php | 29 + src/Factory/APIRequest/ExtendedApiClient.php | 60 + .../ExtendedParcelShopSearchFactory.php | 85 + .../FastParcelShopResponseParser.php | 133 + .../APIRequest/FastParcelShopSearch.php | 123 + src/{Entity => Factory/APIRequest}/index.php | 0 src/Repository/ParcelShopRepository.php | 5 +- src/Service/API/ShipmentApiService.php | 13 +- src/Service/Import/API/ParcelShopImport.php | 161 +- src/Service/Parcel/ParcelUpdateService.php | 379 +- src/Service/PudoService.php | 15 +- translations/lt.php | 2 +- translations/lv.php | 2 +- views/js/admin/import/import_parcels.js | 33 +- .../admin/import/importing-parcels-popup.tpl | 35 + .../hook/admin/partials/pudo-info.tpl | 12 +- .../hook/front/partials/markers-list.tpl | 40 +- 29 files changed, 1288 insertions(+), 4249 deletions(-) delete mode 100644 composer.lock create mode 100644 config/services.yml create mode 100644 src/ConsoleCommand/UpdateParcelShopsCommand.php create mode 100644 src/ConsoleCommand/index.php create mode 100644 src/Factory/APIRequest/ExtendedApiClient.php create mode 100644 src/Factory/APIRequest/ExtendedParcelShopSearchFactory.php create mode 100644 src/Factory/APIRequest/FastParcelShopResponseParser.php create mode 100644 src/Factory/APIRequest/FastParcelShopSearch.php rename src/{Entity => Factory/APIRequest}/index.php (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dad889f..a3948f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -183,6 +183,12 @@ - Improved module performance ## [3.3.1] +- Fixed parcelshop info not displaying in backoffice order view when customer city differs from parcelshop city +- Fixed shipment creation overwriting parcelshop address data with customer address data +- Fixed Lithuanian and Latvian translations for shipping label in admin order panel +- Added Poland parcel shop import support with batch processing +- Fixed null pointer errors when accessing parcel shop data +- Fixed JS variable undefined on standard checkout preventing pickup point selection - Fixed shipment creation failing for accounts requiring Predict SMS service ## [3.3.0] diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 67c4085f..00000000 --- a/composer.lock +++ /dev/null @@ -1,4011 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "d0adbb8ef686f675841e9929585eae6d", - "packages": [ - { - "name": "apimatic/jsonmapper", - "version": "v2.0.3", - "source": { - "type": "git", - "url": "https://github.com/apimatic/jsonmapper.git", - "reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/apimatic/jsonmapper/zipball/f7588f1ab692c402a9118e65cb9fd42b74e5e0db", - "reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "squizlabs/php_codesniffer": "^3.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "apimatic\\jsonmapper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "christian.weiske@netresearch.de", - "homepage": "http://www.netresearch.de/", - "role": "Developer" - }, - { - "name": "Mehdi Jaffery", - "email": "mehdi.jaffery@apimatic.io", - "homepage": "http://apimatic.io/", - "role": "Developer" - } - ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "mehdi.jaffery@apimatic.io", - "issues": "https://github.com/apimatic/jsonmapper/issues", - "source": "https://github.com/apimatic/jsonmapper/tree/v2.0.3" - }, - "time": "2021-07-16T09:02:23+00:00" - }, - { - "name": "apimatic/unirest-php", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/apimatic/unirest-php.git", - "reference": "52e226fb3b7081dc9ef64aee876142a240a5f0f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/apimatic/unirest-php/zipball/52e226fb3b7081dc9ef64aee876142a240a5f0f9", - "reference": "52e226fb3b7081dc9ef64aee876142a240a5f0f9", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.6.0" - }, - "require-dev": { - "phpunit/phpunit": "^5 || ^6 || ^7 || ^8 || ^9" - }, - "suggest": { - "ext-json": "Allows using JSON Bodies for sending and parsing requests" - }, - "type": "library", - "autoload": { - "psr-0": { - "Unirest\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mashape", - "email": "opensource@mashape.com", - "homepage": "https://www.mashape.com", - "role": "Developer" - }, - { - "name": "APIMATIC", - "email": "opensource@apimatic.io", - "homepage": "https://www.apimatic.io", - "role": "Developer" - } - ], - "description": "Unirest PHP", - "homepage": "https://github.com/apimatic/unirest-php", - "keywords": [ - "client", - "curl", - "http", - "https", - "rest" - ], - "support": { - "email": "opensource@apimatic.io", - "issues": "https://github.com/apimatic/unirest-php/issues", - "source": "https://github.com/apimatic/unirest-php/tree/2.3.0" - }, - "time": "2022-06-15T08:29:49+00:00" - }, - { - "name": "invertus/dpdbaltics-api", - "version": "dev-developer", - "source": { - "type": "git", - "url": "https://github.com/Invertus/dpdbaltics-api.git", - "reference": "665c9e8ca25afce5cf9ac1a0a62000826353975d" - }, - "require": { - "apimatic/jsonmapper": "^2.0", - "apimatic/unirest-php": "^2.1", - "ext-json": "*", - "monolog/monolog": "^1.25", - "php": ">=5.6", - "phpdocumentor/reflection-docblock": "^3.0|^4.0", - "psr/log": "^1.1", - "symfony/property-access": "*", - "symfony/property-info": "*", - "symfony/serializer": "*", - "vlucas/phpdotenv": "^3.6" - }, - "require-dev": { - "facebook/webdriver": "dev-master", - "phpunit/phpunit": "*", - "squizlabs/php_codesniffer": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Invertus\\dpdBalticsApi\\": "src/" - } - }, - "authors": [ - { - "name": "Invertus", - "email": "developers@invertus.eu" - } - ], - "time": "2022-09-20T11:10:13+00:00" - }, - { - "name": "invertus/psModuleTabs", - "version": "dev-develop", - "source": { - "type": "git", - "url": "https://github.com/Invertus/ps-module-tabs.git", - "reference": "fbf36f15af6a3cff32b6aaf70426e25066d86115" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Invertus\\psModuleTabs\\": "src/" - } - }, - "authors": [ - { - "name": "Invertus", - "email": "developers@invertus.eu" - } - ], - "time": "2020-07-14T10:55:01+00:00" - }, - { - "name": "monolog/monolog", - "version": "1.27.1", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/904713c5929655dc9b97288b69cfeedad610c9a1", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpstan/phpstan": "^0.12.59", - "phpunit/phpunit": "~4.5", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" - }, - "type": "library", - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/1.27.1" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2022-06-09T08:53:42+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v2.0.21", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" - }, - "time": "2022-02-16T17:07:03+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" - }, - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/3.x" - }, - "time": "2017-11-10T14:09:06+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" - }, - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.7.5", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", - "shasum": "" - }, - "require": { - "php": "^5.5.9 || ^7.0 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.7.5" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2020-07-20T17:29:33+00:00" - }, - { - "name": "prestashop/decimal", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/PrestaShop/decimal.git", - "reference": "188028580f4b551c126d1d723578f3ee88008e95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PrestaShop/decimal/zipball/188028580f4b551c126d1d723578f3ee88008e95", - "reference": "188028580f4b551c126d1d723578f3ee88008e95", - "shasum": "" - }, - "require": { - "php": ">=5.4" - }, - "require-dev": { - "codacy/coverage": "dev-master", - "phpunit/phpunit": "4.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "PrestaShop\\Decimal\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PrestaShop SA", - "email": "contact@prestashop.com" - }, - { - "name": "Pablo Borowicz", - "email": "pablo.borowicz@prestashop.com" - } - ], - "description": "Object-oriented wrapper/shim for BC Math PHP extension. Allows for arbitrary-precision math operations.", - "homepage": "https://github.com/prestashop/decimal", - "keywords": [ - "bcmath", - "decimal", - "math", - "precision", - "prestashop" - ], - "support": { - "issues": "https://github.com/PrestaShop/decimal/issues", - "source": "https://github.com/PrestaShop/decimal/tree/1.4.0" - }, - "time": "2020-10-08T07:14:07+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "symfony/console", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/debug/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "abandoned": "symfony/error-handler", - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/inflector", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/inflector.git", - "reference": "b557c5d061b72cadf454dd87cd1308d0710c8021" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/b557c5d061b72cadf454dd87cd1308d0710c8021", - "reference": "b557c5d061b72cadf454dd87cd1308d0710c8021", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Inflector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Inflector Component", - "homepage": "https://symfony.com", - "keywords": [ - "inflection", - "pluralize", - "singularize", - "string", - "symfony", - "words" - ], - "support": { - "source": "https://github.com/symfony/inflector/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "abandoned": "EnglishInflector from the String component", - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.19-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T09:01:57+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b5f7b932ee6fa802fc792eabd77c4c88084517ce", - "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.19-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.19.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T09:01:57+00:00" - }, - { - "name": "symfony/polyfill-php70", - "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.19-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T09:01:57+00:00" - }, - { - "name": "symfony/property-access", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "f1dc91d0c987f3ba95be1d7874527d11477b25ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/f1dc91d0c987f3ba95be1d7874527d11477b25ff", - "reference": "f1dc91d0c987f3ba95be1d7874527d11477b25ff", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/inflector": "~3.1|~4.0", - "symfony/polyfill-php70": "~1.0" - }, - "require-dev": { - "symfony/cache": "~3.1|~4.0" - }, - "suggest": { - "psr/cache-implementation": "To cache access methods." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony PropertyAccess Component", - "homepage": "https://symfony.com", - "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property path", - "reflection" - ], - "support": { - "source": "https://github.com/symfony/property-access/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/property-info", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-info.git", - "reference": "a5f1e77c881342a5b1e05fdc12642650853bd112" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/a5f1e77c881342a5b1e05fdc12642650853bd112", - "reference": "a5f1e77c881342a5b1e05fdc12642650853bd112", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/inflector": "~3.1|~4.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", - "phpdocumentor/type-resolver": "<0.3.0", - "symfony/dependency-injection": "<3.3" - }, - "require-dev": { - "doctrine/annotations": "~1.7", - "phpdocumentor/reflection-docblock": "^3.0|^4.0", - "symfony/cache": "~3.1|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/serializer": "~2.8|~3.0|~4.0" - }, - "suggest": { - "phpdocumentor/reflection-docblock": "To use the PHPDoc", - "psr/cache-implementation": "To cache results", - "symfony/doctrine-bridge": "To use Doctrine metadata", - "symfony/serializer": "To use Serializer metadata" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyInfo\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Property Info Component", - "homepage": "https://symfony.com", - "keywords": [ - "doctrine", - "phpdoc", - "property", - "symfony", - "type", - "validator" - ], - "support": { - "source": "https://github.com/symfony/property-info/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/serializer", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "6d69ccc1dcfb64c1e9c9444588643e98718d1849" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/6d69ccc1dcfb64c1e9c9444588643e98718d1849", - "reference": "6d69ccc1dcfb64c1e9c9444588643e98718d1849", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "phpdocumentor/type-resolver": "<0.2.1", - "symfony/dependency-injection": "<3.2", - "symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4", - "symfony/property-info": "<3.1", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0", - "symfony/cache": "~3.1|~4.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.2|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/property-access": "~2.8|~3.0|~4.0", - "symfony/property-info": "^3.4.13|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", - "doctrine/cache": "For using the default cached annotation reader and metadata cache.", - "psr/cache-implementation": "For using the metadata cache.", - "symfony/config": "For using the XML mapping loader.", - "symfony/http-foundation": "For using a MIME type guesser within the DataUriNormalizer.", - "symfony/property-access": "For using the ObjectNormalizer.", - "symfony/property-info": "To deserialize relations.", - "symfony/yaml": "For using the default YAML mapping loader." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Serializer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Serializer Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/serializer/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v3.6.10", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "5b547cdb25825f10251370f57ba5d9d924e6f68e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/5b547cdb25825f10251370f57ba5d9d924e6f68e", - "reference": "5b547cdb25825f10251370f57ba5d9d924e6f68e", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0", - "phpoption/phpoption": "^1.5.2", - "symfony/polyfill-ctype": "^1.17" - }, - "require-dev": { - "ext-filter": "*", - "ext-pcre": "*", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.21" - }, - "suggest": { - "ext-filter": "Required to use the boolean validator.", - "ext-pcre": "Required to use most of the library." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "https://github.com/vlucas" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "support": { - "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v3.6.10" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], - "time": "2021-12-12T23:02:06+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.9.1" - }, - "time": "2020-07-08T17:02:28+00:00" - } - ], - "packages-dev": [ - { - "name": "composer/pcre", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.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/1.0.1" - }, - "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": "2022-01-21T20:24:37+00:00" - }, - { - "name": "composer/semver", - "version": "3.4.0", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" - }, - "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": "2023-08-31T09:50:34+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a", - "shasum": "" - }, - "require": { - "composer/pcre": "^1", - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" - }, - "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": "2022-02-24T20:20:32+00:00" - }, - { - "name": "doctrine/annotations", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/v1.4.0" - }, - "time": "2017-02-24T16:22:25+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.0.5" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2015-06-14T21:17:01+00:00" - }, - { - "name": "doctrine/lexer", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "^4.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.0.2" - }, - "time": "2019-06-08T11:03:04+00:00" - }, - { - "name": "facebook/webdriver", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/php-webdriver/php-webdriver-archive.git", - "reference": "575600dfcfebad49fd0fc59d781b0696462a1f4e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver-archive/zipball/575600dfcfebad49fd0fc59d781b0696462a1f4e", - "reference": "575600dfcfebad49fd0fc59d781b0696462a1f4e", - "shasum": "" - }, - "require": { - "php": ">=5.3.19" - }, - "require-dev": { - "phpdocumentor/phpdocumentor": "2.*", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "autoload": { - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A php client for WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "support": { - "forum": "https://www.facebook.com/groups/phpwebdriver/", - "issues": "https://github.com/facebook/php-webdriver/issues", - "source": "https://github.com/facebook/php-webdriver" - }, - "abandoned": "php-webdriver/webdriver", - "time": "2015-06-09T17:09:16+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v2.19.3", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "75ac86f33fab4714ea5a39a396784d83ae3b5ed8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/75ac86f33fab4714ea5a39a396784d83ae3b5ed8", - "reference": "75ac86f33fab4714ea5a39a396784d83ae3b5ed8", - "shasum": "" - }, - "require": { - "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.2 || ^2.0", - "doctrine/annotations": "^1.2", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^5.6 || ^7.0 || ^8.0", - "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.43 || ^4.1.6 || ^5.0", - "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^3.0 || ^4.0 || ^5.0", - "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", - "symfony/polyfill-php70": "^1.0", - "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0 || ^5.0", - "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.4", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.4.2", - "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy-phpunit": "^1.1 || ^2.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.13 || ^9.5", - "phpunitgoodpractices/polyfill": "^1.5", - "phpunitgoodpractices/traits": "^1.9.1", - "sanmai/phpunit-legacy-adapter": "^6.4 || ^8.2.1", - "symfony/phpunit-bridge": "^5.2.1", - "symfony/yaml": "^3.0 || ^4.0 || ^5.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters.", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", - "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.19-dev" - } - }, - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - }, - "classmap": [ - "tests/Test/AbstractFixerTestCase.php", - "tests/Test/AbstractIntegrationCaseFactory.php", - "tests/Test/AbstractIntegrationTestCase.php", - "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php", - "tests/Test/IntegrationCaseFactoryInterface.php", - "tests/Test/InternalIntegrationCaseFactory.php", - "tests/Test/IsIdenticalConstraint.php", - "tests/Test/TokensWithObservedTransformers.php", - "tests/TestCase.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "support": { - "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.19.3" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2021-11-15T17:17:55+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.x" - }, - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v3.1.5", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", - "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "~4.0|~5.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v3.1.5" - }, - "time": "2018-02-28T20:30:58+00:00" - }, - { - "name": "php-cs-fixer/diff", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/dbd31aeb251639ac0b9e7e29405c1441907f5759", - "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "symfony/process": "^3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "SpacePossum" - } - ], - "description": "sebastian/diff v2 backport support for PHP5.6", - "homepage": "https://github.com/PHP-CS-Fixer", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/PHP-CS-Fixer/diff/issues", - "source": "https://github.com/PHP-CS-Fixer/diff/tree/v1.3.1" - }, - "abandoned": true, - "time": "2020-10-14T08:39:05+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" - }, - "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-xdebug": "^2.5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/4.0" - }, - "time": "2017-04-02T07:44:40+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/master" - }, - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.12", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/1.4" - }, - "abandoned": true, - "time": "2017-12-04T08:55:13+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "5.7.27", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "^1.0.6|^2.0.1", - "symfony/yaml": "~2.1|~3.0|~4.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/5.7.27" - }, - "time": "2018-02-01T05:50:59+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/3.4" - }, - "abandoned": true, - "time": "2017-06-30T09:13:00+00:00" - }, - { - "name": "prestashop/autoindex", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/PrestaShopCorp/autoindex.git", - "reference": "92e10242f94a99163dece280f6bd7b7c2b79c158" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PrestaShopCorp/autoindex/zipball/92e10242f94a99163dece280f6bd7b7c2b79c158", - "reference": "92e10242f94a99163dece280f6bd7b7c2b79c158", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^3.1", - "php": ">=5.6", - "symfony/console": "^3.4", - "symfony/finder": "^3.4" - }, - "bin": [ - "bin/autoindex" - ], - "type": "library", - "autoload": { - "psr-4": { - "PrestaShop\\AutoIndex\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "AFL-3.0" - ], - "authors": [ - { - "name": "PrestaShop SA", - "email": "contact@prestashop.com" - } - ], - "description": "Automatically add an 'index.php' in all the current or specified directories and all sub-directories.", - "homepage": "https://github.com/PrestaShopCorp/autoindex", - "support": { - "source": "https://github.com/PrestaShopCorp/autoindex/tree/v1.0.0" - }, - "time": "2020-03-11T13:37:03+00:00" - }, - { - "name": "prestashop/header-stamp", - "version": "v1.7", - "source": { - "type": "git", - "url": "https://github.com/PrestaShopCorp/header-stamp.git", - "reference": "d77ce6d0a7f066670a4774be88f05e5f07b4b6fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PrestaShopCorp/header-stamp/zipball/d77ce6d0a7f066670a4774be88f05e5f07b4b6fc", - "reference": "d77ce6d0a7f066670a4774be88f05e5f07b4b6fc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^3.1", - "php": ">=5.6", - "symfony/console": "^3.4 || ~4.0 || ~5.0", - "symfony/finder": "^3.4 || ~4.0 || ~5.0" - }, - "require-dev": { - "prestashop/php-dev-tools": "1.*" - }, - "bin": [ - "bin/header-stamp" - ], - "type": "library", - "autoload": { - "psr-4": { - "PrestaShop\\HeaderStamp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "AFL-3.0" - ], - "authors": [ - { - "name": "PrestaShop SA", - "email": "contact@prestashop.com" - } - ], - "description": "Rewrite your file headers to add the license or to make them up-to-date", - "homepage": "https://github.com/PrestaShopCorp/header-stamp", - "support": { - "issues": "https://github.com/PrestaShopCorp/header-stamp/issues", - "source": "https://github.com/PrestaShopCorp/header-stamp/tree/v1.7" - }, - "time": "2020-12-09T16:40:38+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "1.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" - }, - "time": "2017-01-29T09:50:25+00:00" - }, - { - "name": "sebastian/diff", - "version": "1.4.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/1.4" - }, - "time": "2017-05-22T07:24:03+00:00" - }, - { - "name": "sebastian/environment", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/master" - }, - "time": "2016-11-26T07:53:53+00:00" - }, - { - "name": "sebastian/exporter", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/master" - }, - "time": "2016-11-19T08:54:04+00:00" - }, - { - "name": "sebastian/global-state", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/1.1.1" - }, - "time": "2015-10-12T03:26:01+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/master" - }, - "time": "2017-02-18T15:18:39+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" - }, - "time": "2016-11-19T07:33:16+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" - }, - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "31fde73757b6bad247c54597beef974919ec6860" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/31fde73757b6bad247c54597beef974919ec6860", - "reference": "31fde73757b6bad247c54597beef974919ec6860", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/dependency-injection": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/debug": "~3.4|~4.4", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e58d7841cddfed6e846829040dca2cca0ebbbbb3", - "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/finder", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", - "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-11-16T17:02:08+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", - "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony OptionsResolver Component", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "beecef6b463b06954638f02378f52496cb84bacc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", - "reference": "beecef6b463b06954638f02378f52496cb84bacc", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.19-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T09:01:57+00:00" - }, - { - "name": "symfony/process", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/298b81faad4ce60e94466226b2abbb8c9bca7462", - "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Stopwatch Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "88289caa3c166321883f67fe5130188ebbb47094" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", - "reference": "88289caa3c166321883f67fe5130188ebbb47094", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "invertus/dpdbaltics-api": 20, - "invertus/psmoduletabs": 20, - "facebook/webdriver": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.6" - }, - "platform-dev": [], - "platform-overrides": { - "php": "5.6" - }, - "plugin-api-version": "2.2.0" -} diff --git a/config/command.yml b/config/command.yml index f8cd8e74..f6aa5600 100644 --- a/config/command.yml +++ b/config/command.yml @@ -3,9 +3,10 @@ services: public: true invertus.dpdbaltics.console_command.update_parcel_shops_command: - class : 'Invertus\dpdBaltics\ConsoleCommand\UpdateParcelShopsCommand' + class: 'Invertus\dpdBaltics\ConsoleCommand\UpdateParcelShopsCommand' arguments: - '@invertus.dpdbaltics.logger.logger' - '@invertus.dpdbaltics.service.import.api.parcel_shop_import' + - '@invertus.dpdbaltics.provider.zone_range_provider' tags: - - { name: 'console.command', command: 'dpdbaltics:update-parcel-shops'} + - { name: 'console.command', command: 'dpdbaltics:update-parcel-shops' } diff --git a/config/requestFactory.yml b/config/requestFactory.yml index d9b50611..26e8d32b 100644 --- a/config/requestFactory.yml +++ b/config/requestFactory.yml @@ -30,7 +30,7 @@ services: invertus.dpdbaltics_api.factory.apirequest.parcel_shop_search_factory: - class: 'Invertus\dpdBalticsApi\Factory\APIRequest\ParcelShopSearchFactory' + class: 'Invertus\dpdBaltics\Factory\APIRequest\ExtendedParcelShopSearchFactory' arguments: - '@invertus.dpdbaltics.logger.logger' - '@invertus.dpdbaltics.factory.apiparams_factory' diff --git a/config/service.yml b/config/service.yml index 1271387d..7cfbbfea 100644 --- a/config/service.yml +++ b/config/service.yml @@ -223,7 +223,7 @@ services: class: 'Invertus\dpdBaltics\Service\Parcel\ParcelUpdateService' arguments: - '@invertus.dpdbaltics.repository.parcel_shop_repository' - + - '@invertus.dpdbaltics.logger.logger' invertus.dpdbaltics.service.parcel.parcel_shop_service: class: 'Invertus\dpdBaltics\Service\Parcel\ParcelShopService' arguments: @@ -249,8 +249,9 @@ services: - '@invertus.dpdbaltics.service.api.parcel_shop_search_api_service' - '@invertus.dpdbaltics.service.parcel.parcel_update_service' - '@dpdbaltics' + - '@invertus.dpdbaltics.logger.logger' - # AccessibilityChecker + # AccessibilityChecker invertus.dpdbaltics.grid.row.print_accessibility_checker: class: 'Invertus\dpdBaltics\Grid\Row\PrintAccessibilityChecker' arguments: @@ -283,6 +284,7 @@ services: arguments: - '@invertus.dpdbaltics.factory.parcel_tracking_url_factory' - '@dpdbaltics' + - '@invertus.dpdbaltics.logger.logger' invertus.dpdbaltics.service.label_printing_service: class: 'Invertus\dpdBaltics\Service\LabelPrintingService' diff --git a/config/services.yml b/config/services.yml new file mode 100644 index 00000000..ffec09f2 --- /dev/null +++ b/config/services.yml @@ -0,0 +1,10 @@ +services: + _defaults: + public: true + autowire: false + autoconfigure: false + + Invertus\dpdBaltics\ConsoleCommand\UpdateParcelShopsCommand: + class: 'Invertus\dpdBaltics\ConsoleCommand\UpdateParcelShopsCommand' + tags: + - { name: 'console.command' } diff --git a/controllers/admin/AdminDPDBalticsAjaxController.php b/controllers/admin/AdminDPDBalticsAjaxController.php index d7ffdaf1..af0eb23a 100644 --- a/controllers/admin/AdminDPDBalticsAjaxController.php +++ b/controllers/admin/AdminDPDBalticsAjaxController.php @@ -48,11 +48,72 @@ public function ajaxProcessImportZones() public function ajaxProcessImportParcels() { + $countryId = Tools::getValue('countryId'); + $countryIso = Country::getIsoById($countryId); + + // Validate country + if (empty($countryIso)) { + $this->ajaxDie(json_encode([ + 'success' => false, + 'error' => $this->module->l('Invalid country selected', 'AdminDPDBalticsAjaxController') + ])); + return; + } + + $countryIso = strtoupper($countryIso); + /** @var ParcelShopImport $parcelShopImport */ $parcelShopImport = $this->module->getModuleContainer('invertus.dpdbaltics.service.import.api.parcel_shop_import'); - $countryId = Tools::getValue('countryId'); - $countryIso = Country::getIsoById($countryId); - $this->ajaxDie(json_encode($parcelShopImport->importParcelShops($countryIso))); + try { + $result = $parcelShopImport->importParcelShops($countryIso); + $this->ajaxDie(json_encode($result)); + } catch (\Exception $e) { + $this->handleImportError($e, $countryIso); + } catch (\Error $e) { + $this->handleImportError($e, $countryIso); + } } + + /** + * Handle import errors with helpful messages for timeout scenarios. + * + * @param \Exception|\Error $e + * @param string $countryIso + */ + private function handleImportError($e, $countryIso) + { + $errorMsg = $e->getMessage(); + $isTimeout = stripos($errorMsg, 'timeout') !== false + || stripos($errorMsg, 'execution time') !== false + || stripos($errorMsg, 'Maximum execution') !== false; + + if ($isTimeout) { + $this->ajaxDie(json_encode($this->buildCronRequiredResponse($countryIso))); + } else { + $this->ajaxDie(json_encode([ + 'success' => false, + 'error' => $e instanceof \Error + ? 'PHP Error: ' . $errorMsg + : 'Error: ' . $errorMsg + ])); + } + } + + /** + * Build response for when cron is required (timeout or large country). + * + * @param string $countryIso + * @return array + */ + private function buildCronRequiredResponse($countryIso) + { + return [ + 'success' => false, + 'error' => $this->module->l('This country requires automatic updates.', 'AdminDPDBalticsAjaxController'), + 'requires_cron' => true, + 'cron_command' => 'php bin/console dpdbaltics:update-parcel-shops --country=' . $countryIso + ]; + } + } diff --git a/controllers/admin/AdminDPDBalticsImportExportController.php b/controllers/admin/AdminDPDBalticsImportExportController.php index ba4239c3..265220d8 100644 --- a/controllers/admin/AdminDPDBalticsImportExportController.php +++ b/controllers/admin/AdminDPDBalticsImportExportController.php @@ -136,16 +136,7 @@ protected function initOptions() $this->module->getLocalPath() . 'views/templates/admin/partials/break.tpl' ); - $href = $this->context->link->getModuleLink( - $this->module->name, - 'CronJob', - [ - 'action' => 'updateParcelShops', - 'token' => Configuration::get(Config::DPDBALTICS_HASH_TOKEN) - ] - ); - $cronJobText = - $this->module->l('You can setup cronjob with: ' . $href); + $cronJobText = $this->module->l('For large countries (e.g. Poland), we recommend automatic daily updates via cron:') . ' php bin/console dpdbaltics:update-parcel-shops --all'; if (Shop::CONTEXT_GROUP == $shopContext) { $info = $this->module->l('Data will be imported to all group shops'); diff --git a/controllers/front/CronJob.php b/controllers/front/CronJob.php index 7525f309..e4439ce8 100644 --- a/controllers/front/CronJob.php +++ b/controllers/front/CronJob.php @@ -27,18 +27,33 @@ exit; } +/** + * @deprecated Use CLI command instead for reliable imports without timeout issues: + * php bin/console dpdbaltics:update-parcel-shops --all + * + * This HTTP-based cron endpoint may timeout for large countries (PL). + * The CLI command has no timeout limitations. + */ class DpdbalticsCronJobModuleFrontController extends AbstractFrontController { public function postProcess() { - set_time_limit(0); + // Note: We intentionally do NOT use set_time_limit() here because: + // 1. It doesn't work on many servers (disabled in php.ini) + // 2. wget/curl have their own timeout limits anyway + // For reliable imports, use CLI: php bin/console dpdbaltics:update-parcel-shops --all - $token = Tools::getValue('token'); - if ($token !== Configuration::get(Config::DPDBALTICS_HASH_TOKEN)) { - $this->ajaxDie([ + $token = (string) Tools::getValue('token'); + $expectedToken = Configuration::get(Config::DPDBALTICS_HASH_TOKEN); + + // Use hash_equals to prevent timing attacks + // Ensure both values are strings for PHP 8 compatibility + if (empty($expectedToken) || empty($token) || !hash_equals((string) $expectedToken, $token)) { + $this->ajaxDie(json_encode([ 'success' => false, - 'message' => 'wrong token' - ]); + 'message' => 'Invalid token' + ])); + return; } $action = Tools::getValue('action'); @@ -50,6 +65,8 @@ public function postProcess() $zoneRangeProvider = $this->module->getModuleContainer('invertus.dpdbaltics.provider.zone_range_provider'); $countriesInZoneRange = $zoneRangeProvider->getAllZoneRangesCountryIsoCodes(); + $response = ['success' => true, 'message' => 'No countries to import']; + if ($countriesInZoneRange) { foreach ($countriesInZoneRange as $country) { $response = $parcelShopImport->importParcelShops($country); @@ -70,7 +87,10 @@ public function postProcess() break; default: - return; + $this->ajaxDie(json_encode([ + 'success' => false, + 'message' => 'Unknown action. For parcel shop import, use CLI: php bin/console dpdbaltics:update-parcel-shops --all' + ])); } } } diff --git a/dpdbaltics.php b/dpdbaltics.php index 7a1d2241..39c72f17 100644 --- a/dpdbaltics.php +++ b/dpdbaltics.php @@ -214,6 +214,12 @@ public function hookActionFrontControllerSetMedia() 'isOnePageCheckout' => $opcModuleCompatibilityValidator->isOpcModuleInUse() ] ]); + } else { + Media::addJsDef([ + 'dpdbaltics' => [ + 'isOnePageCheckout' => false + ] + ]); } /** @var \Invertus\dpdBaltics\Provider\CurrentCountryProvider $currentCountryProvider */ @@ -956,12 +962,13 @@ private function displayInAdminOrderPage(array $params) /** @var null|\Invertus\dpdBalticsApi\Api\DTO\Object\ParcelShop $selectedPudoService */ $selectedPudoService = null; $hasParcelShops = false; - if ($parcelShops) { - if ($selectedPudo->pudo_id) { - $selectedPudoService = $parcelShopService->getParcelShopByShopId($selectedPudo->pudo_id)[0]; - } else { - $selectedPudoService = $parcelShops[0]; - } + if ($selectedPudo->pudo_id) { + $pudoShops = $parcelShopService->getParcelShopByShopId($selectedPudo->pudo_id); + $selectedPudoService = !empty($pudoShops) ? $pudoShops[0] : null; + } elseif ($parcelShops) { + $selectedPudoService = isset($parcelShops[0]) ? $parcelShops[0] : null; + } + if ($selectedPudoService || $parcelShops) { $hasParcelShops = true; } diff --git a/src/Config/Config.php b/src/Config/Config.php index da755019..67023e47 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -160,6 +160,8 @@ class Config const FETCH_PUDO_POINT = 1; const RETRIEVE_OPENING_HOURS = 1; + const SKIP_OPENING_HOURS = 0; + const COUNTRIES_SKIP_OPENING_HOURS = ['PL']; const MAXIMUM_PUDO_POINTS_IN_MAP = 30; diff --git a/src/ConsoleCommand/UpdateParcelShopsCommand.php b/src/ConsoleCommand/UpdateParcelShopsCommand.php new file mode 100644 index 00000000..5d3093ed --- /dev/null +++ b/src/ConsoleCommand/UpdateParcelShopsCommand.php @@ -0,0 +1,237 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\ConsoleCommand; + +use Country; +use Invertus\dpdBaltics\Provider\ZoneRangeProvider; +use Invertus\dpdBaltics\Service\Import\API\ParcelShopImport; +use Module; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * CLI command for importing parcel shops. + * + * Usage: + * php bin/console dpdbaltics:update-parcel-shops --country=PL + * php bin/console dpdbaltics:update-parcel-shops --all + * + * Cron setup (daily at 2 AM): + * 0 2 * * * cd /var/www/html && php bin/console dpdbaltics:update-parcel-shops --all + */ +class UpdateParcelShopsCommand extends Command +{ + protected static $defaultName = 'dpdbaltics:update-parcel-shops'; + + /** + * @var LoggerInterface|null + */ + private $logger; + + /** + * @var ParcelShopImport|null + */ + private $parcelShopImport; + + /** + * @var ZoneRangeProvider|null + */ + private $zoneRangeProvider; + + /** + * @var \DPDBaltics|null + */ + private $module; + + protected function configure() + { + $this + ->setDescription('Import/update DPD parcel shops from API') + ->addOption( + 'country', + 'c', + InputOption::VALUE_OPTIONAL, + 'Country ISO code to import (e.g., PL, LT, LV, EE)' + ) + ->addOption( + 'all', + 'a', + InputOption::VALUE_NONE, + 'Import all default countries (LT, LV, EE, PL)' + ) + ->setHelp(<<<'EOF' +The %command.name% command imports parcel shops from DPD API. + +Import a single country: + php %command.full_name% --country=PL + +Import all countries from configured zone ranges: + php %command.full_name% --all + +Cron setup (daily at 2 AM): + 0 2 * * * cd /var/www/html && php bin/console dpdbaltics:update-parcel-shops --all + +This command is recommended for importing parcel shops as it has no timeout limitations +unlike the web interface which may timeout for countries with many parcel shops. +EOF + ); + } + + /** + * Initialize services from the module's container. + */ + private function initServices() + { + if ($this->module !== null) { + return; + } + + $this->module = Module::getInstanceByName('dpdbaltics'); + + if (!$this->module) { + throw new \RuntimeException('DPD Baltics module is not installed or not active.'); + } + + $this->parcelShopImport = $this->module->getService('invertus.dpdbaltics.service.import.api.parcel_shop_import'); + $this->zoneRangeProvider = $this->module->getService('invertus.dpdbaltics.provider.zone_range_provider'); + $this->logger = $this->module->getService('invertus.dpdbaltics.logger.logger'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + // Initialize services from module's container + try { + $this->initServices(); + } catch (\Exception $e) { + $output->writeln(sprintf('%s', $e->getMessage())); + return 1; + } + + $country = $input->getOption('country'); + $all = $input->getOption('all'); + + if (!$country && !$all) { + $output->writeln('Please specify --country=XX or --all'); + $output->writeln(''); + $output->writeln('Examples:'); + $output->writeln(' php bin/console dpdbaltics:update-parcel-shops --country=PL'); + $output->writeln(' php bin/console dpdbaltics:update-parcel-shops --all'); + return 1; + } + + // Get countries to import + if ($all) { + $countries = $this->getCountriesToImport(); + if (empty($countries)) { + $output->writeln('No countries configured in zone ranges.'); + $output->writeln('Please configure zone ranges in DPD module settings first.'); + return 1; + } + } else { + $countries = [strtoupper($country)]; + } + $totalStartTime = microtime(true); + $hasError = false; + + $output->writeln(''); + $output->writeln('DPD Parcel Shop Import'); + $output->writeln(str_repeat('=', 50)); + $output->writeln(''); + + foreach ($countries as $countryCode) { + $output->write(sprintf('Importing %s... ', $countryCode)); + + $startTime = microtime(true); + + try { + $result = $this->parcelShopImport->importParcelShops($countryCode); + + $elapsed = round(microtime(true) - $startTime, 1); + + if (isset($result['success']) && $result['success']) { + $output->writeln(sprintf('OK (%ss)', $elapsed)); + if (isset($result['success_message'])) { + $output->writeln(sprintf(' %s', $result['success_message'])); + } + } else { + $hasError = true; + $output->writeln(sprintf('FAILED (%ss)', $elapsed)); + if (isset($result['error'])) { + $output->writeln(sprintf(' %s', $result['error'])); + } + } + } catch (\Exception $e) { + $hasError = true; + $elapsed = round(microtime(true) - $startTime, 1); + $output->writeln(sprintf('ERROR (%ss)', $elapsed)); + $output->writeln(sprintf(' %s', $e->getMessage())); + + if ($this->logger) { + $this->logger->error(sprintf( + '[CLI] Import failed for %s: %s', + $countryCode, + $e->getMessage() + )); + } + } + + $output->writeln(''); + } + + $totalElapsed = round(microtime(true) - $totalStartTime, 1); + $output->writeln(str_repeat('=', 50)); + $output->writeln(sprintf('Total time: %ss', $totalElapsed)); + $output->writeln(''); + + return $hasError ? 1 : 0; + } + + /** + * Get countries to import from zone range configuration. + * Falls back to active countries if no zone ranges configured. + * + * @return array + */ + private function getCountriesToImport() + { + // First try to get countries from zone ranges (same as CronJob.php) + $countries = $this->zoneRangeProvider->getAllZoneRangesCountryIsoCodes(); + + if (!empty($countries)) { + return $countries; + } + + // Fallback: get all active countries + $activeCountries = Country::getCountries((int) \Configuration::get('PS_LANG_DEFAULT'), true); + $countryCodes = []; + + foreach ($activeCountries as $country) { + if (!empty($country['iso_code'])) { + $countryCodes[] = strtoupper($country['iso_code']); + } + } + + return $countryCodes; + } +} diff --git a/src/ConsoleCommand/index.php b/src/ConsoleCommand/index.php new file mode 100644 index 00000000..3c82fdf6 --- /dev/null +++ b/src/ConsoleCommand/index.php @@ -0,0 +1,29 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/src/Factory/APIRequest/ExtendedApiClient.php b/src/Factory/APIRequest/ExtendedApiClient.php new file mode 100644 index 00000000..9a834fb9 --- /dev/null +++ b/src/Factory/APIRequest/ExtendedApiClient.php @@ -0,0 +1,60 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\Factory\APIRequest; + +use Invertus\dpdBalticsApi\Factory\APIRequest\ApiClient; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Extended API client with configurable timeout. + * Used to override the hardcoded 20-second timeout in the vendor package + * for large API requests like Poland parcel shop imports. + */ +class ExtendedApiClient extends ApiClient +{ + /** + * @var int + */ + private $timeout; + + /** + * @param string $url + * @param string $username + * @param string $password + * @param int $timeout Timeout in seconds (default 120 for large imports) + */ + public function __construct($url, $username, $password, $timeout = 120) + { + parent::__construct($url, $username, $password); + $this->timeout = $timeout; + } + + /** + * @return int + */ + public function getTimeout() + { + return $this->timeout; + } +} diff --git a/src/Factory/APIRequest/ExtendedParcelShopSearchFactory.php b/src/Factory/APIRequest/ExtendedParcelShopSearchFactory.php new file mode 100644 index 00000000..1d9d2507 --- /dev/null +++ b/src/Factory/APIRequest/ExtendedParcelShopSearchFactory.php @@ -0,0 +1,85 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\Factory\APIRequest; + +use Invertus\dpdBalticsApi\Api\ApiRequest; +use Invertus\dpdBalticsApi\Factory\APIParamsFactoryInterface; +use Invertus\dpdBalticsApi\Factory\APIRequest\ParcelShopSearchFactory; +use Psr\Log\LoggerInterface; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Extended factory for ParcelShopSearch that uses: + * 1. Longer API timeout (120s instead of 20s) + * 2. Fast response parser (bypasses slow Symfony serializer) + * + * This allows importing large countries like Poland (3000+ parcel shops) + * even on servers with 30-second PHP timeout limits. + */ +class ExtendedParcelShopSearchFactory extends ParcelShopSearchFactory +{ + const PARCEL_SHOP_SEARCH_TIMEOUT = 120; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var APIParamsFactoryInterface + */ + private $APIParamsFactory; + + public function __construct(LoggerInterface $logger, APIParamsFactoryInterface $APIParamsFactory) + { + parent::__construct($logger, $APIParamsFactory); + $this->logger = $logger; + $this->APIParamsFactory = $APIParamsFactory; + } + + /** + * Create FastParcelShopSearch with extended timeout and fast parser. + * + * @return FastParcelShopSearch + */ + public function makeParcelShopSearch() + { + $httpClientFactory = new ExtendedApiClient( + $this->APIParamsFactory->getUrl(), + $this->APIParamsFactory->getUsername(), + $this->APIParamsFactory->getPassword(), + self::PARCEL_SHOP_SEARCH_TIMEOUT + ); + + $apiRequest = new ApiRequest( + $httpClientFactory, + $this->logger, + $this->APIParamsFactory->getModuleVersion(), + $this->APIParamsFactory->getPSVersion() + ); + + // Use FastParcelShopSearch with simple JSON parser instead of slow Symfony serializer + return new FastParcelShopSearch($apiRequest, $this->logger); + } +} diff --git a/src/Factory/APIRequest/FastParcelShopResponseParser.php b/src/Factory/APIRequest/FastParcelShopResponseParser.php new file mode 100644 index 00000000..f3257682 --- /dev/null +++ b/src/Factory/APIRequest/FastParcelShopResponseParser.php @@ -0,0 +1,133 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\Factory\APIRequest; + +use Invertus\dpdBalticsApi\Api\DTO\Object\ParcelShop; +use Invertus\dpdBalticsApi\Api\DTO\Response\ParcelShopSearchResponse; +use stdClass; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Fast parser for ParcelShopSearchResponse that bypasses slow Symfony serializer. + * Uses simple property assignment instead of ReflectionExtractor. + * + * Performance: ~100x faster than Symfony serializer for large datasets. + */ +class FastParcelShopResponseParser +{ + /** + * Parse API response array into ParcelShopSearchResponse object. + * This is much faster than Symfony's ObjectNormalizer with ReflectionExtractor. + * + * @param array $responseData Raw response data from API + * @return ParcelShopSearchResponse + */ + public function parse(array $responseData) + { + $response = new ParcelShopSearchResponse(); + + if (isset($responseData['status'])) { + $response->setStatus($responseData['status']); + } + + if (isset($responseData['errlog'])) { + $response->setErrLog($responseData['errlog']); + } + + $parcelShops = []; + if (isset($responseData['parcelshops']) && is_array($responseData['parcelshops'])) { + foreach ($responseData['parcelshops'] as $shopData) { + $parcelShops[] = $this->parseParcelShop($shopData); + } + } + + $response->setParcelShops($parcelShops); + + return $response; + } + + /** + * Parse single parcel shop data into ParcelShop object. + * + * @param array|object $data + * @return ParcelShop + */ + private function parseParcelShop($data) + { + // Handle both array and object (stdClass) formats + $data = (array) $data; + + $shop = new ParcelShop(); + + // PHP 5.6 compatible - use isset() instead of ?? operator + $shop->setParcelShopId(isset($data['parcelshop_id']) ? $data['parcelshop_id'] : null); + $shop->setCompany(isset($data['company']) ? $data['company'] : null); + $shop->setCountry(isset($data['country']) ? $data['country'] : null); + $shop->setCity(isset($data['city']) ? $data['city'] : null); + $shop->setPCode(isset($data['pcode']) ? $data['pcode'] : null); + $shop->setStreet(isset($data['street']) ? $data['street'] : null); + $shop->setEmail(isset($data['email']) ? $data['email'] : null); + $shop->setPhone(isset($data['phone']) ? $data['phone'] : null); + $shop->setDistance(isset($data['distance']) ? $data['distance'] : null); + $shop->setLongitude(isset($data['longitude']) ? $data['longitude'] : null); + $shop->setLatitude(isset($data['latitude']) ? $data['latitude'] : null); + $shop->setCoordinateX(isset($data['coordinateX']) ? $data['coordinateX'] : null); + $shop->setCoordinateY(isset($data['coordinateY']) ? $data['coordinateY'] : null); + $shop->setCoordinateZ(isset($data['coordinateZ']) ? $data['coordinateZ'] : null); + + $openingHours = array(); + if (isset($data['openingHours']) && is_array($data['openingHours'])) { + foreach ($data['openingHours'] as $hoursData) { + $openingHours[] = $this->parseOpeningHours($hoursData); + } + } + $shop->setOpeningHours($openingHours); + + return $shop; + } + + /** + * Parse opening hours data into stdClass object. + * Using stdClass because existing code accesses properties directly (e.g., $item->weekday) + * + * @param array|object $data + * @return stdClass + */ + private function parseOpeningHours($data) + { + // Handle both array and object (stdClass) formats + $data = (array) $data; + + // Create stdClass to match expected property access pattern + // PHP 5.6 compatible - use isset() instead of ?? operator + $hours = new stdClass(); + $hours->weekday = isset($data['weekday']) ? $data['weekday'] : null; + $hours->openMorning = isset($data['openMorning']) ? $data['openMorning'] : null; + $hours->closeMorning = isset($data['closeMorning']) ? $data['closeMorning'] : null; + $hours->openAfternoon = isset($data['openAfternoon']) ? $data['openAfternoon'] : null; + $hours->closeAfternoon = isset($data['closeAfternoon']) ? $data['closeAfternoon'] : null; + + return $hours; + } +} diff --git a/src/Factory/APIRequest/FastParcelShopSearch.php b/src/Factory/APIRequest/FastParcelShopSearch.php new file mode 100644 index 00000000..c05fc797 --- /dev/null +++ b/src/Factory/APIRequest/FastParcelShopSearch.php @@ -0,0 +1,123 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\Factory\APIRequest; + +use Exception; +use Invertus\dpdBalticsApi\Api\ApiRequest; +use Invertus\dpdBalticsApi\Api\DTO\Request\ParcelShopSearchRequest; +use Invertus\dpdBalticsApi\Api\DTO\Response\ParcelShopSearchResponse; +use Invertus\dpdBalticsApi\ApiConfig\ApiConfig; +use Invertus\dpdBalticsApi\Exception\DPDBalticsAPIException; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Fast ParcelShopSearch that uses simple JSON parsing instead of slow Symfony serializer. + * This can handle large responses (3000+ parcel shops) without timeout. + */ +class FastParcelShopSearch +{ + /** + * @var ApiRequest + */ + private $apiRequest; + + /** + * @var FastParcelShopResponseParser + */ + private $parser; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param ApiRequest $apiRequest + * @param LoggerInterface|null $logger + */ + public function __construct(ApiRequest $apiRequest, $logger = null) + { + $this->apiRequest = $apiRequest; + $this->parser = new FastParcelShopResponseParser(); + $this->logger = $logger instanceof LoggerInterface ? $logger : new NullLogger(); + } + + /** + * Search for parcel shops using fast parsing. + * + * @param ParcelShopSearchRequest $request + * @return ParcelShopSearchResponse + * @throws DPDBalticsAPIException + */ + public function parcelShopSearch(ParcelShopSearchRequest $request) + { + try { + $response = $this->apiRequest->post( + ApiConfig::SQ_PARCEL_SHOP_SEARCH, + [ + 'query' => $request->jsonSerialize(), + 'verify' => false, + ] + ); + } catch (Exception $e) { + $this->logger->error(sprintf( + '[FastParcelShopSearch] API call FAILED | Error: %s', + $e->getMessage() + )); + + throw new DPDBalticsAPIException( + 'An error occurred trying to search for parcel shops: ' . $e->getMessage(), + DPDBalticsAPIException::PARCEL_SHOP_SEARCH, + $e + ); + } + + // Handle null or empty response + if (empty($response)) { + $this->logger->error('[FastParcelShopSearch] API returned empty response'); + $emptyResponse = new ParcelShopSearchResponse(); + $emptyResponse->setStatus('err'); + $emptyResponse->setErrLog('API returned empty response'); + $emptyResponse->setParcelShops(array()); + return $emptyResponse; + } + + // Convert stdClass to array (Unirest returns decoded JSON as object) + $responseData = json_decode(json_encode($response), true); + + // Handle JSON conversion failure + if (!is_array($responseData)) { + $this->logger->error('[FastParcelShopSearch] Failed to convert response to array'); + $errorResponse = new ParcelShopSearchResponse(); + $errorResponse->setStatus('err'); + $errorResponse->setErrLog('Failed to parse API response'); + $errorResponse->setParcelShops(array()); + return $errorResponse; + } + + return $this->parser->parse($responseData); + } +} diff --git a/src/Entity/index.php b/src/Factory/APIRequest/index.php similarity index 100% rename from src/Entity/index.php rename to src/Factory/APIRequest/index.php diff --git a/src/Repository/ParcelShopRepository.php b/src/Repository/ParcelShopRepository.php index e18df98f..9e126db2 100644 --- a/src/Repository/ParcelShopRepository.php +++ b/src/Repository/ParcelShopRepository.php @@ -33,9 +33,8 @@ class ParcelShopRepository extends AbstractEntityRepository { public function deleteShopsByCountryCode($countryCode) { - - $sql = 'DELETE w FROM `' . _DB_PREFIX_ . 'dpd_shop_work_hours` w - INNER JOIN `' . _DB_PREFIX_ . 'dpd_shop` s ON s.country = "' . pSQL($countryCode) . '" + $sql = 'DELETE w FROM `' . _DB_PREFIX_ . 'dpd_shop_work_hours` w + INNER JOIN `' . _DB_PREFIX_ . 'dpd_shop` s ON s.country = "' . pSQL($countryCode) . '" WHERE s.parcel_shop_id = w.parcel_shop_id'; if (!Db::getInstance()->execute($sql)) { diff --git a/src/Service/API/ShipmentApiService.php b/src/Service/API/ShipmentApiService.php index 21cfa714..6000b28f 100644 --- a/src/Service/API/ShipmentApiService.php +++ b/src/Service/API/ShipmentApiService.php @@ -108,15 +108,18 @@ public function createShipment($addressId, ShipmentData $shipmentData, $orderId) $hasAddressFields = (bool) !$postCode || !$firstName || !$address->city || !$country; // Post code might be wrong in order adress, so we set terminal post code instead + $selectedParcel = null; if ($shipmentData->isPudo()) { $parcel = $this->parcelShopService->getParcelShopByShopId($shipmentData->getSelectedPudoId()); - $selectedParcel = is_array($parcel) ? reset($parcel) : $parcel; - $postCode = $selectedParcel->getPCode(); - $address->address1 = $selectedParcel->getStreet(); + $selectedParcel = is_array($parcel) && !empty($parcel) ? reset($parcel) : $parcel; + if ($selectedParcel && is_object($selectedParcel)) { + $postCode = $selectedParcel->getPCode(); + $address->address1 = $selectedParcel->getStreet(); + } } // IF prestashop allows, we take selected parcel terminal address in case information is missing in checkout address in specific cases. - if (($hasAddressFields) && $shipmentData->isPudo()) { + if (($hasAddressFields) && $shipmentData->isPudo() && $selectedParcel && is_object($selectedParcel)) { $firstName = $selectedParcel->getCompany(); $address->address1 = $selectedParcel->getStreet(); $address->city = $selectedParcel->getCity(); @@ -197,7 +200,7 @@ public function createReturnServiceShipment($addressTemplateId, $orderId, Shipme $address1 = $selectedPudo->street; $city = $selectedPudo->city; $countryIso = $selectedPudo->country_code; - $postCode = $selectedPudo->post_code; + $postCode = preg_replace('/[^0-9]/', '', $selectedPudo->post_code); } else { $address1 = $address->address1; $city = $address->city; diff --git a/src/Service/Import/API/ParcelShopImport.php b/src/Service/Import/API/ParcelShopImport.php index 49d29c94..26756353 100644 --- a/src/Service/Import/API/ParcelShopImport.php +++ b/src/Service/Import/API/ParcelShopImport.php @@ -21,15 +21,13 @@ namespace Invertus\dpdBaltics\Service\Import\API; -use Configuration; use DPDBaltics; use EntityAddException; -use Exception; use Invertus\dpdBaltics\Config\Config; use Invertus\dpdBaltics\Service\API\ParcelShopSearchApiService; use Invertus\dpdBaltics\Service\Parcel\ParcelUpdateService; use Invertus\dpdBalticsApi\Api\DTO\Response\ParcelShopSearchResponse; -use Tools; +use Psr\Log\LoggerInterface; if (!defined('_PS_VERSION_')) { exit; @@ -43,61 +41,170 @@ class ParcelShopImport * @var ParcelShopSearchApiService */ private $apiService; + /** * @var ParcelUpdateService */ private $parcelUpdateService; + /** * @var DPDBaltics */ private $module; + /** + * @var LoggerInterface + */ + private $logger; + public function __construct( ParcelShopSearchApiService $apiService, ParcelUpdateService $parcelUpdateService, - DPDBaltics $module + DPDBaltics $module, + LoggerInterface $logger ) { $this->apiService = $apiService; $this->parcelUpdateService = $parcelUpdateService; $this->module = $module; + $this->logger = $logger; } + /** + * Import parcel shops for a country. + * + * @param string $selectedCountry Country ISO code + * @return array + */ public function importParcelShops($selectedCountry) { + $startTime = microtime(true); + + $retrieveOpeningHours = $this->shouldRetrieveOpeningHours($selectedCountry); + /** @var ParcelShopSearchResponse $shops */ $shops = $this->apiService->getAllCountryParcels( $selectedCountry, Config::FETCH_PUDO_POINT, - Config::RETRIEVE_OPENING_HOURS + $retrieveOpeningHours ); + + $apiTime = round(microtime(true) - $startTime, 2); + if ($shops->getStatus() === Config::API_RESPONSE_ERROR_STATUS) { - return - [ - 'success' => false, - 'error' => sprintf($this->module->l('Failed to update parcel shops: %s', self::FILE_NAME), $shops->getErrLog()) - ]; + $this->logger->error(sprintf( + '[ParcelImport] API ERROR for %s | Error: %s | API took: %ss', + $selectedCountry, + $shops->getErrLog(), + $apiTime + )); + + return [ + 'success' => false, + 'error' => sprintf($this->module->l('Failed to update parcel shops: %s', self::FILE_NAME), $shops->getErrLog()) + ]; + } + + $parcelShops = $shops->getParcelShops(); + + if ($parcelShops === null || !is_array($parcelShops)) { + $this->logger->error(sprintf( + '[ParcelImport] API returned NO DATA for %s | API took: %ss', + $selectedCountry, + $apiTime + )); + + return [ + 'success' => false, + 'error' => sprintf($this->module->l('Failed to update parcel shops: API returned no data for country %s', self::FILE_NAME), $selectedCountry) + ]; } + + $parcelCount = count($parcelShops); + $dbStartTime = microtime(true); + try { - $this->parcelUpdateService->updateParcels($shops->getParcelShops(), $selectedCountry); + $this->parcelUpdateService->updateParcels($parcelShops, $selectedCountry); } catch (EntityAddException $e) { - return - [ - 'success' => false, - 'error' => $e->getMessage() - ]; + $totalTime = round(microtime(true) - $startTime, 2); + $dbTime = round(microtime(true) - $dbStartTime, 2); + + $this->logger->error(sprintf( + '[ParcelImport] DATABASE ERROR for %s | Error: %s | DB time: %ss | Total time: %ss', + $selectedCountry, + $e->getMessage(), + $dbTime, + $totalTime + )); + + return [ + 'success' => false, + 'error' => $e->getMessage() + ]; + } catch (\Exception $e) { + $totalTime = round(microtime(true) - $startTime, 2); + $dbTime = round(microtime(true) - $dbStartTime, 2); + + $this->logger->error(sprintf( + '[ParcelImport] EXCEPTION for %s | Type: %s | Error: %s | DB time: %ss | Total time: %ss', + $selectedCountry, + get_class($e), + $e->getMessage(), + $dbTime, + $totalTime + )); + + return [ + 'success' => false, + 'error' => $e->getMessage() + ]; } catch (\Error $e) { - return - [ - 'success' => false, - 'error' => $e->getMessage() - ]; - } + $totalTime = round(microtime(true) - $startTime, 2); + $dbTime = round(microtime(true) - $dbStartTime, 2); + + $this->logger->error(sprintf( + '[ParcelImport] PHP ERROR for %s | Type: %s | Error: %s | File: %s:%d | DB time: %ss | Total time: %ss', + $selectedCountry, + get_class($e), + $e->getMessage(), + $e->getFile(), + $e->getLine(), + $dbTime, + $totalTime + )); - return - [ - 'success' => true, - 'success_message' => $this->module->l('Successfully updated parcel shops', self::FILE_NAME) + return [ + 'success' => false, + 'error' => $e->getMessage() ]; + } + + $totalTime = round(microtime(true) - $startTime, 2); + + return [ + 'success' => true, + 'success_message' => sprintf( + $this->module->l('Successfully imported %d parcel shops in %ss', self::FILE_NAME), + $parcelCount, + $totalTime + ) + ]; } -} \ No newline at end of file + /** + * Check if opening hours should be retrieved for this country. + * Large countries may timeout when retrieving opening hours due to API limits. + * + * @param string $countryIso + * @return int + */ + private function shouldRetrieveOpeningHours($countryIso) + { + $countryIso = strtoupper($countryIso); + + if (in_array($countryIso, Config::COUNTRIES_SKIP_OPENING_HOURS, true)) { + return Config::SKIP_OPENING_HOURS; + } + + return Config::RETRIEVE_OPENING_HOURS; + } +} diff --git a/src/Service/Parcel/ParcelUpdateService.php b/src/Service/Parcel/ParcelUpdateService.php index 062dd7b4..514138ea 100644 --- a/src/Service/Parcel/ParcelUpdateService.php +++ b/src/Service/Parcel/ParcelUpdateService.php @@ -1,143 +1,236 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 - */ - - -namespace Invertus\dpdBaltics\Service\Parcel; - -use DPDShop; -use DPDShopWorkHours; -use EntityAddException; -use Exception; -use Invertus\dpdBaltics\Repository\ParcelShopRepository; -use Invertus\dpdBalticsApi\Api\DTO\Object\OpeningHours; -use Invertus\dpdBalticsApi\Api\DTO\Object\ParcelShop; - -if (!defined('_PS_VERSION_')) { - exit; -} - -class ParcelUpdateService -{ - - /** - * @var ParcelShopRepository - */ - private $parcelShopRepository; - - public function __construct(ParcelShopRepository $parcelShopRepository) - { - $this->parcelShopRepository = $parcelShopRepository; - } - - public function updateParcels(array $parcels, $countryCode) - { - $isDeleteSuccess = $this->parcelShopRepository->deleteShopsByCountryCode($countryCode); - if (!$isDeleteSuccess) { - return false; - } - - foreach ($parcels as $parcel) { - if ($parcel instanceof ParcelShop) { - $this->addParcelShop($parcel); - } else { - $parcelShop = $this->resetParcelObject($parcel); - $this->addParcelShop($parcelShop); - } - } - - return true; - } - - public function addParcelShop(ParcelShop $parcel) - { - $parcelShop = new DPDShop(); - $parcelShop->parcel_shop_id = $parcel->getParcelShopId(); - $parcelShop->company = $parcel->getCompany(); - $parcelShop->country = $parcel->getCountry(); - $parcelShop->city = $parcel->getCity(); - $parcelShop->p_code = $parcel->getPCode(); - $parcelShop->street = $parcel->getStreet(); - $parcelShop->email = $parcel->getEmail(); - $parcelShop->phone = $parcel->getPhone(); - $parcelShop->longitude = $parcel->getLongitude(); - $parcelShop->latitude = $parcel->getLatitude(); - - try { - $parcelShop->add(); - } catch (Exception $e) { - throw new EntityAddException( - 'Failed to add parcel shop', - EntityAddException::DPD_PARCEL_SHOP_EXCEPTION, - $e - ); - } - - foreach ($parcel->getOpeningHours() as $openingHours) { - $parcelShopWorkHours = new DPDShopWorkHours(); - $parcelShopWorkHours->parcel_shop_id = $parcel->getParcelShopId(); - $parcelShopWorkHours->week_day = $openingHours->weekday; - $parcelShopWorkHours->open_morning = $openingHours->openMorning; - $parcelShopWorkHours->close_morning = $openingHours->closeMorning; - $parcelShopWorkHours->open_afternoon = $openingHours->openAfternoon; - $parcelShopWorkHours->close_afternoon = $openingHours->closeAfternoon; - - try { - $parcelShopWorkHours->add(); - } catch (Exception $e) { - throw new EntityAddException( - 'Failed to add parcel shop work hours', - EntityAddException::DPD_PARCEL_SHOP_WORK_HOURS_EXCEPTION, - $e - ); - } - } - - return true; - } - - /** - *This function is needed for prestashop versions below 1704 as API response loses object instance - * - * @param $parcel - * - * @return ParcelShop - */ - private function resetParcelObject($parcel) - { - $parcelShop = new ParcelShop(); - $parcelShop->setParcelShopId($parcel->parcelshop_id); - $parcelShop->setCompany($parcel->company); - $parcelShop->setCountry($parcel->country); - $parcelShop->setCity($parcel->city); - $parcelShop->setPCode($parcel->pcode); - $parcelShop->setStreet($parcel->street); - $parcelShop->setEmail($parcel->email); - $parcelShop->setPhone($parcel->phone); - $parcelShop->setDistance($parcel->distance); - $parcelShop->setLongitude($parcel->longitude); - $parcelShop->setLatitude($parcel->latitude); - $parcelShop->setCoordinateX($parcel->coordinateX); - $parcelShop->setCoordinateY($parcel->coordinateY); - $parcelShop->setCoordinateZ($parcel->coordinateZ); - $parcelShop->setOpeningHours($parcel->openingHours); - - return $parcelShop; - } -} + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace Invertus\dpdBaltics\Service\Parcel; + +use Db; +use EntityAddException; +use Invertus\dpdBaltics\Repository\ParcelShopRepository; +use Invertus\dpdBalticsApi\Api\DTO\Object\ParcelShop; +use Psr\Log\LoggerInterface; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class ParcelUpdateService +{ + const BATCH_SIZE = 100; + + /** + * @var ParcelShopRepository + */ + private $parcelShopRepository; + + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct(ParcelShopRepository $parcelShopRepository, LoggerInterface $logger) + { + $this->parcelShopRepository = $parcelShopRepository; + $this->logger = $logger; + } + + /** + * Update parcels using batch insert for better performance + * + * @param array $parcels + * @param string $countryCode + * @return bool + * @throws EntityAddException + */ + public function updateParcels(array $parcels, $countryCode) + { + $isDeleteSuccess = $this->parcelShopRepository->deleteShopsByCountryCode($countryCode); + + if (!$isDeleteSuccess) { + $this->logger->error(sprintf( + '[ParcelUpdate] FAILED to delete existing shops for %s', + $countryCode + )); + return false; + } + + $shopsBatch = []; + $workHoursBatch = []; + + foreach ($parcels as $parcel) { + if (!($parcel instanceof ParcelShop)) { + $parcel = $this->resetParcelObject($parcel); + } + + $shopsBatch[] = $this->prepareShopData($parcel); + + $openingHours = $parcel->getOpeningHours(); + if (is_array($openingHours) && !empty($openingHours)) { + foreach ($openingHours as $openingHoursItem) { + $workHoursBatch[] = $this->prepareWorkHoursData($parcel->getParcelShopId(), $openingHoursItem); + } + } + + if (count($shopsBatch) >= self::BATCH_SIZE) { + $this->insertShopsBatch($shopsBatch); + $shopsBatch = []; + } + + if (count($workHoursBatch) >= self::BATCH_SIZE * 7) { + $this->insertWorkHoursBatch($workHoursBatch); + $workHoursBatch = []; + } + } + + if (!empty($shopsBatch)) { + $this->insertShopsBatch($shopsBatch); + } + + if (!empty($workHoursBatch)) { + $this->insertWorkHoursBatch($workHoursBatch); + } + + return true; + } + + /** + * Prepare shop data for batch insert + * + * @param ParcelShop $parcel + * @return array + */ + private function prepareShopData(ParcelShop $parcel) + { + return [ + 'parcel_shop_id' => pSQL($parcel->getParcelShopId()), + 'company' => pSQL($parcel->getCompany()), + 'country' => pSQL($parcel->getCountry()), + 'city' => pSQL($parcel->getCity()), + 'p_code' => pSQL($parcel->getPCode()), + 'street' => pSQL($parcel->getStreet()), + 'email' => pSQL($parcel->getEmail()), + 'phone' => pSQL($parcel->getPhone()), + 'longitude' => pSQL($parcel->getLongitude()), + 'latitude' => pSQL($parcel->getLatitude()), + ]; + } + + /** + * Prepare work hours data for batch insert + * + * @param string $parcelShopId + * @param object $openingHours + * @return array + */ + private function prepareWorkHoursData($parcelShopId, $openingHours) + { + return [ + 'parcel_shop_id' => pSQL($parcelShopId), + 'week_day' => pSQL($openingHours->weekday), + 'open_morning' => pSQL($openingHours->openMorning), + 'close_morning' => pSQL($openingHours->closeMorning), + 'open_afternoon' => pSQL($openingHours->openAfternoon), + 'close_afternoon' => pSQL($openingHours->closeAfternoon), + ]; + } + + /** + * Insert shops batch + * + * @param array $batch + * @throws EntityAddException + */ + private function insertShopsBatch(array $batch) + { + if (empty($batch)) { + return; + } + + $result = Db::getInstance()->insert('dpd_shop', $batch); + + if (!$result) { + $this->logger->error(sprintf( + '[ParcelUpdate] FAILED to insert shops batch | Batch size: %d | DB error: %s', + count($batch), + Db::getInstance()->getMsgError() + )); + + throw new EntityAddException( + 'Failed to add parcel shops batch: ' . Db::getInstance()->getMsgError(), + EntityAddException::DPD_PARCEL_SHOP_EXCEPTION + ); + } + } + + /** + * Insert work hours batch + * + * @param array $batch + * @throws EntityAddException + */ + private function insertWorkHoursBatch(array $batch) + { + if (empty($batch)) { + return; + } + + $result = Db::getInstance()->insert('dpd_shop_work_hours', $batch); + + if (!$result) { + $this->logger->error(sprintf( + '[ParcelUpdate] FAILED to insert work hours batch | Batch size: %d | DB error: %s', + count($batch), + Db::getInstance()->getMsgError() + )); + + throw new EntityAddException( + 'Failed to add parcel shop work hours batch: ' . Db::getInstance()->getMsgError(), + EntityAddException::DPD_PARCEL_SHOP_WORK_HOURS_EXCEPTION + ); + } + } + + /** + * This function is needed for prestashop versions below 1704 as API response loses object instance + * + * @param $parcel + * @return ParcelShop + */ + private function resetParcelObject($parcel) + { + $parcelShop = new ParcelShop(); + $parcelShop->setParcelShopId($parcel->parcelshop_id); + $parcelShop->setCompany($parcel->company); + $parcelShop->setCountry($parcel->country); + $parcelShop->setCity($parcel->city); + $parcelShop->setPCode($parcel->pcode); + $parcelShop->setStreet($parcel->street); + $parcelShop->setEmail($parcel->email); + $parcelShop->setPhone($parcel->phone); + $parcelShop->setDistance($parcel->distance); + $parcelShop->setLongitude($parcel->longitude); + $parcelShop->setLatitude($parcel->latitude); + $parcelShop->setCoordinateX($parcel->coordinateX); + $parcelShop->setCoordinateY($parcel->coordinateY); + $parcelShop->setCoordinateZ($parcel->coordinateZ); + $parcelShop->setOpeningHours($parcel->openingHours); + + return $parcelShop; + } +} diff --git a/src/Service/PudoService.php b/src/Service/PudoService.php index a150b347..324115fb 100644 --- a/src/Service/PudoService.php +++ b/src/Service/PudoService.php @@ -256,9 +256,18 @@ public function savePudoOrder($productId, $pudoId, $isoCode, $cartId, $city, $st $pudoOrder->id_carrier = $carrier->id; $pudoOrder->country_code = $countryCode; $pudoOrder->id_cart = $cartId; - $pudoOrder->city = $city; - $pudoOrder->street = $street; - $pudoOrder->post_code = $zipCode; + + $pudoShop = DPDShop::getShopByPudoId($pudoId); + if ($pudoShop && $pudoShop->id) { + $pudoOrder->city = $pudoShop->city; + $pudoOrder->street = $pudoShop->street; + $pudoOrder->post_code = $pudoShop->p_code; + } else { + $pudoOrder->city = $city; + $pudoOrder->street = $street; + $pudoOrder->post_code = $zipCode; + } + $pudoOrder->save(); } diff --git a/translations/lt.php b/translations/lt.php index e98d6e51..728f025f 100755 --- a/translations/lt.php +++ b/translations/lt.php @@ -75,7 +75,7 @@ $_MODULE['<{dpdbaltics}prestashop>markers-list_f4ec5f57bd4d31b803312d873be40da9'] = 'Keisti'; $_MODULE['<{dpdbaltics}prestashop>markers-list_b689ff1685e45b92f4cbff6570d96647'] = 'Darbo valandos:'; $_MODULE['<{dpdbaltics}prestashop>markers-list_6b9d52ad995244cbf32d9cc75aadbded'] = 'Nerasta jokių atsiėmimo taškų'; -$_MODULE['<{dpdbaltics}prestashop>admin-order_48300820b47f97208960c55d476de0d1'] = 'dpdbaltics laivyba'; +$_MODULE['<{dpdbaltics}prestashop>admin-order_48300820b47f97208960c55d476de0d1'] = 'dpdbaltics siuntimas'; $_MODULE['<{dpdbaltics}prestashop>admin-order_5e34f5e134425db6f1d6a68370aab105'] = '[išplėsti]'; $_MODULE['<{dpdbaltics}prestashop>admin-order_ed5c01a46e47055b81a911c21915af84'] = 'BANDYMO UŽSAKYMAS'; $_MODULE['<{dpdbaltics}prestashop>admin-order_6bb311efd788bb4b3123896667e767a7'] = 'Siuntimas'; diff --git a/translations/lv.php b/translations/lv.php index 47437fb6..7418b182 100644 --- a/translations/lv.php +++ b/translations/lv.php @@ -72,7 +72,7 @@ $_MODULE['<{dpdbaltics}prestashop>markers-list_f4ec5f57bd4d31b803312d873be40da9'] = 'Mainīt'; $_MODULE['<{dpdbaltics}prestashop>markers-list_b689ff1685e45b92f4cbff6570d96647'] = 'Darba stundas:'; $_MODULE['<{dpdbaltics}prestashop>markers-list_6b9d52ad995244cbf32d9cc75aadbded'] = 'Netika atrasti saņemšanas punkti'; -$_MODULE['<{dpdbaltics}prestashop>admin-order_48300820b47f97208960c55d476de0d1'] = 'dpdbaltics kuģniecība'; +$_MODULE['<{dpdbaltics}prestashop>admin-order_48300820b47f97208960c55d476de0d1'] = 'dpdbaltics piegāde'; $_MODULE['<{dpdbaltics}prestashop>admin-order_5e34f5e134425db6f1d6a68370aab105'] = '[izvērst]'; $_MODULE['<{dpdbaltics}prestashop>admin-order_ed5c01a46e47055b81a911c21915af84'] = 'TESTA PASŪTĪJUMS'; $_MODULE['<{dpdbaltics}prestashop>admin-order_6bb311efd788bb4b3123896667e767a7'] = 'Sūtījums'; diff --git a/views/js/admin/import/import_parcels.js b/views/js/admin/import/import_parcels.js index 226a0d31..c3f99b35 100644 --- a/views/js/admin/import/import_parcels.js +++ b/views/js/admin/import/import_parcels.js @@ -24,6 +24,7 @@ $(window).load(function () { $('.import-parcels-button').click(function () { $.ajax(dpdAjaxUrl, { method: 'POST', + timeout: 360000, // 6 minutes - allows for API call (120s) + parsing + DB operations data: { ajax: 1, countryId: countryId, @@ -36,11 +37,29 @@ $(window).load(function () { loadImport(); }, success: function(response) { - response = JSON.parse(response); - if (response.success) { - showSuccessMessage(response.success_message); + try { + if (typeof response === 'string') { + response = JSON.parse(response); + } + if (response.success) { + showSuccessMessage(response.success_message); + } else if (response.requires_cron) { + showCronRequiredModal(response.cron_command); + } else { + showErrorMessage(response.error || 'Unknown error occurred'); + } + } catch (e) { + showErrorMessage('Failed to parse server response: ' + e.message); + } + }, + error: function(xhr, status, error) { + if (status === 'timeout' || xhr.status === 500 || xhr.status === 504) { + // Server timeout - show cron modal + showCronRequiredModal('php bin/console dpdbaltics:update-parcel-shops --all'); + } else if (xhr.status === 0) { + showErrorMessage('Network error. Please check your connection.'); } else { - showErrorMessage(response.error); + showErrorMessage('Import failed: ' + (error || status || 'Unknown error')); } }, complete: function () { @@ -67,6 +86,12 @@ $(window).load(function () { clearInterval(toggleInterval); } + function showCronRequiredModal(cronCommand) { + var $modal = $('#import-cron-required-modal'); + $('#cron-command-display').text(cronCommand || 'php bin/console dpdbaltics:update-parcel-shops --all'); + $modal.modal('show'); + } + function nextOnBoardStep(nextStep) { $.ajax(onBoard.ajaxUrl, { method: 'POST', diff --git a/views/templates/admin/import/importing-parcels-popup.tpl b/views/templates/admin/import/importing-parcels-popup.tpl index 94fc1af7..a710d100 100644 --- a/views/templates/admin/import/importing-parcels-popup.tpl +++ b/views/templates/admin/import/importing-parcels-popup.tpl @@ -54,3 +54,38 @@ + + diff --git a/views/templates/hook/admin/partials/pudo-info.tpl b/views/templates/hook/admin/partials/pudo-info.tpl index 100d4934..a7a89dff 100644 --- a/views/templates/hook/admin/partials/pudo-info.tpl +++ b/views/templates/hook/admin/partials/pudo-info.tpl @@ -16,6 +16,7 @@ * @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 *} +{if $selectedPudo}
@@ -68,4 +69,13 @@
- \ No newline at end of file + +{else} +
+
+
+ {l s='No pickup point selected' mod='dpdbaltics'} +
+
+
+{/if} diff --git a/views/templates/hook/front/partials/markers-list.tpl b/views/templates/hook/front/partials/markers-list.tpl index b89fbd63..d0a8b963 100644 --- a/views/templates/hook/front/partials/markers-list.tpl +++ b/views/templates/hook/front/partials/markers-list.tpl @@ -44,22 +44,24 @@
-
-

- {l s='more information' mod='dpdbaltics'} -

-
+ {if $service->getCountry() != 'PL'} +
+

+ {l s='more information' mod='dpdbaltics'} +

+
+ {/if}
{else} -
-
- {l s='No pickup points found' mod='dpdbaltics'} +
+
+ {l s='Select a city to view pickup points' mod='dpdbaltics'}
{/if} From 9ac490e3597a2ceaa705a2a067616b7d99291ac8 Mon Sep 17 00:00:00 2001 From: Gytautas Zumaras <96050852+GytisZum@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:07:31 +0200 Subject: [PATCH 07/28] fix: add additional validation for the supercheckout module which using currentcontroller variable (#153) --- views/js/front/modules/supercheckout.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/views/js/front/modules/supercheckout.js b/views/js/front/modules/supercheckout.js index 1f712f0a..aefc5c7d 100644 --- a/views/js/front/modules/supercheckout.js +++ b/views/js/front/modules/supercheckout.js @@ -16,6 +16,13 @@ * @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 */ + +// Fix for PrestaShop 8.x and 9.x compatibility +// currentController global variable was removed in PS 1.7.7+ and doesn't exist in PS 8/9 +if (typeof window.currentController === 'undefined') { + window.currentController = ''; +} + function isDPdCarrierSelected() { if (document.querySelector('.supercheckout_shipping_option:checked')) { var selectedCarrierValue = parseInt(document.querySelector('.supercheckout_shipping_option:checked').value); From 46157262eded9a9807a2cfa64945a5f5313134e9 Mon Sep 17 00:00:00 2001 From: Marijus Dilys <106698165+MarijusDilys@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:07:51 +0200 Subject: [PATCH 08/28] fix (#150) --- views/css/front/pudo-shipment.css | 1 + 1 file changed, 1 insertion(+) diff --git a/views/css/front/pudo-shipment.css b/views/css/front/pudo-shipment.css index be2c6e7e..9f5ddb08 100644 --- a/views/css/front/pudo-shipment.css +++ b/views/css/front/pudo-shipment.css @@ -146,6 +146,7 @@ border-radius: 4px; width: 100%; background-color: white; + -webkit-appearance: none !important; } .search-block-container .form-control { From fcd607df9f518c18e0c2dc1e34357e4dac55a6b4 Mon Sep 17 00:00:00 2001 From: webotron Date: Wed, 25 Mar 2026 10:08:00 +0200 Subject: [PATCH 09/28] change switch expression to use correct method; add filename to translate method to make translation work (#161) --- controllers/front/Ajax.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/controllers/front/Ajax.php b/controllers/front/Ajax.php index a17c1c77..0a911b87 100644 --- a/controllers/front/Ajax.php +++ b/controllers/front/Ajax.php @@ -84,7 +84,7 @@ public function postProcess() try { $response = $this->searchPudoServices($countryCode, $city, $carrierId, $cartId); } catch (Exception $e) { - $this->messages[] = $this->module->l('Parcel shop search failed!'); + $this->messages[] = $this->module->l('Parcel shop search failed!', self::FILENAME); $this->ajaxDie(json_encode( [ 'status' => false, @@ -157,7 +157,7 @@ public function postProcess() try { $response = $this->searchPudoServices($countryCode, $city, $carrierId, $cartId, $street); } catch (Exception $e) { - $this->messages[] = $this->module->l('Parcel shop search failed!'); + $this->messages[] = $this->module->l('Parcel shop search failed!', self::FILENAME); $this->ajaxDie(json_encode( [ 'status' => false, @@ -250,7 +250,7 @@ private function savePudoPickupPoint($pudoId, $countryCode) ); if (!$addPudoCartOrderStatus) { - $this->messages[] = $this->l('Failed to save pickup point.'); + $this->messages[] = $this->l('Failed to save pickup point.', self::FILENAME); $this->ajaxDie(json_encode([ 'template' => $this->getMessageTemplate('danger'), 'status' => false @@ -375,24 +375,24 @@ private function saveParcelShop($countryCode, $city, $street) */ private function setErrorMessage($exception) { - switch ($exception->getMessage()) { + switch ($exception->getCode()) { case Config::ERROR_COULD_NOT_SAVE_PHONE_NUMBER: - $this->messages[] = $this->module->l('Could not save phone number'); + $this->messages[] = $this->module->l('Could not save phone number', self::FILENAME); break; case Config::ERROR_BAD_PHONE_NUMBER_PREFIX: - $this->messages[] = $this->module->l('Phone number prefix is empty'); + $this->messages[] = $this->module->l('Phone number prefix is empty', self::FILENAME); break; case Config::ERROR_PHONE_EMPTY: - $this->messages[] = $this->module->l('Phone number is empty'); + $this->messages[] = $this->module->l('Phone number is empty', self::FILENAME); break; case Config::ERROR_PHONE_HAS_INVALID_CHARACTERS: - $this->messages[] = $this->module->l('Phone number contains invalid characters'); + $this->messages[] = $this->module->l('Phone number contains invalid characters', self::FILENAME); break; case Config::ERROR_PHONE_HAS_INVALID_LENGTH: - $this->messages[] = $this->module->l('Phone number length is invalid'); + $this->messages[] = $this->module->l('Phone number length is invalid', self::FILENAME); break; case Config::ERROR_INVALID_PUDO_TERMINAL: - $this->messages[] = $this->module->l('Pudo point is missing, please select valid terminal point'); + $this->messages[] = $this->module->l('Pudo point is missing, please select valid terminal point', self::FILENAME); break; default: $this->messages[] = $exception->getMessage(); From fd6f05e8bb009415f837ceffac6bc84d49d45633 Mon Sep 17 00:00:00 2001 From: Marijus Dilys <106698165+MarijusDilys@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:08:58 +0200 Subject: [PATCH 10/28] Fix automatic PUDO point pre-selection in LIST mode (#149) Added placeholder option to street dropdown requiring explicit selection before loading PUDO points, preventing customer confusion from unintended pre-selection. --- views/js/front/pudo-search.js | 10 +++++++--- .../hook/front/partials/pudo-search-street.tpl | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/views/js/front/pudo-search.js b/views/js/front/pudo-search.js index ef907e65..431fc6c9 100644 --- a/views/js/front/pudo-search.js +++ b/views/js/front/pudo-search.js @@ -30,7 +30,9 @@ $(document).ready(function () { $(document).on('change', 'select[name="dpd-street"]', function () { var city = $('select[name="dpd-city"]').val(); var street = $('select[name="dpd-street"]').val(); - saveSelectedStreet(city, street); + if (street) { + saveSelectedStreet(city, street); + } }); $(document).on('keyup', 'input[name="dpd-street"]', function () { @@ -100,8 +102,10 @@ function updateStreetSelect(city) { $streetSelectDiv.empty().append(response.template); $('select.chosen-select').chosen({inherit_select_classes: true}); var street = $('select[name="dpd-street"]').val(); - saveSelectedStreet(city, street); - isPudoPointSelected = true; + if (street) { + saveSelectedStreet(city, street); + isPudoPointSelected = true; + } } }, error: function (response) { diff --git a/views/templates/hook/front/partials/pudo-search-street.tpl b/views/templates/hook/front/partials/pudo-search-street.tpl index cc3cf730..384722c3 100644 --- a/views/templates/hook/front/partials/pudo-search-street.tpl +++ b/views/templates/hook/front/partials/pudo-search-street.tpl @@ -27,6 +27,9 @@