diff --git a/.gitattributes b/.gitattributes index 6c17557..d684144 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,5 @@ /tests export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/phpunit.xml \ No newline at end of file +/phpunit.xml export-ignore +/phpstan.neon export-ignore \ No newline at end of file diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..bab8f6c --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,17 @@ +name: Run Shepherd + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: php-actions/composer@v6 + - uses: php-actions/phpstan@v3 + with: + path: src/ tests/ + level: 9 + + \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b252927..70f04c7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,32 +14,34 @@ jobs: strategy: fail-fast: true matrix: - php: [8.3, 8.2, 8.1, 8.0] + php: [8.3, 8.2, 8.1] dependency-version: [prefer-lowest, prefer-stable] - guzzle: [7.*, 6.2] - name: PHP${{ matrix.php }} - G${{ matrix.guzzle }} - ${{ matrix.dependency-version }} + name: PHP${{ matrix.php }} - ${{ matrix.dependency-version }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: - path: ~/.composer/cache/files - key: dependencies-guzzle-${{ matrix.guzzle }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + path: ${{ steps.composer-cache.outputs.dir }} + key: php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: curl coverage: none - - name: Install dependencies + - name: Install prefered version dependencies run: | - composer require "guzzlehttp/guzzle:${{ matrix.guzzle }}" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Execute tests diff --git a/.github/workflows/update-license.yml b/.github/workflows/update-license.yml index 8ee1898..70134f9 100644 --- a/.github/workflows/update-license.yml +++ b/.github/workflows/update-license.yml @@ -8,7 +8,7 @@ jobs: update-license-year: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: FantasticFiasco/action-update-license-year@v2 diff --git a/.gitignore b/.gitignore index 17f8862..5518e80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -.idea/ +/tests/cache/ - -# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,composer -# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,composer +# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,composer,phpunit,phpstorm+all,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,composer,phpunit,phpstorm+all,visualstudiocode ### Composer ### composer.phar @@ -10,7 +9,7 @@ composer.phar # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file -# composer.lock +composer.lock ### Linux ### *~ @@ -60,6 +59,128 @@ Temporary Items # iCloud generated files *.icloud +### PhpStorm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PhpStorm+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### PHPUnit ### +# Covers PHPUnit +# Reference: https://phpunit.de/ + +# Generated files +.phpunit.result.cache +.phpunit.cache + +# PHPUnit +/app/phpunit.xml +/phpunit.xml + +# Build data +/build/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + ### Windows ### # Windows thumbnail cache files Thumbs.db @@ -86,4 +207,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,composer +# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,composer,phpunit,phpstorm+all,visualstudiocode diff --git a/LICENSE b/LICENSE index c2a338b..e85dca7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 CLICKSPORTS GmbH +Copyright (c) 2023 Sysix Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 3dc862e..95c5ce2 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,72 @@ # Lexoffice PHP API -![tests](https://github.com/clicksports/lexoffice-php-api/workflows/tests/badge.svg) -[![Latest Stable Version](https://poser.pugx.org/clicksports/lex-office-api/v)](//packagist.org/packages/clicksports/lex-office-api) -[![License](https://poser.pugx.org/clicksports/lex-office-api/license)](//packagist.org/packages/clicksports/lex-office-api) +![tests](https://github.com/sysix/lexoffice-php-api/workflows/tests/badge.svg) +[![Latest Stable Version](https://poser.pugx.org/sysix/lex-office-api/v)](//packagist.org/packages/sysix/lex-office-api) +[![License](https://poser.pugx.org/sysix/lex-office-api/license)](//packagist.org/packages/sysix/lex-office-api) ## Requirements -PHP: >= 7.4 -Extensions: [Composer](https://getcomposer.org/), [PHP-JSON](https://www.php.net/manual/en/book.json.php) +PHP: >= 8.1 +Extensions: +- [Composer](https://getcomposer.org/) +- [PHP-JSON](https://www.php.net/manual/en/book.json.php) +- [PSR-18 HTTP-Client](https://packagist.org/providers/psr/http-client-implementation) ## Install composer: -`composer require clicksports/lex-office-api` +`composer require sysix/lex-office-api` ## Usage -Search for the official API Documentation [here](https://developers.lexoffice.io/docs/). +Search for the [official API Documentation](https://developers.lexoffice.io/docs/). You need an [API Key](https://app.lexoffice.de/addons/public-api) for that. ### Basic ```php -$apiKey = getenv('LEX_OFFICE_API_KEY'); // store keys in .env file -$api = new \Clicksports\LexOffice\Api($apiKey); +// store keys in .env file +$apiKey = getenv('LEX_OFFICE_API_KEY'); + +// in this example we are using guzzlehttp/guzzle package, it can be any PSR-18 HTTP Client +// see: https://packagist.org/providers/psr/http-client-implementation +$httpClient = \GuzzleHttp\Client(); +$api = new \Sysix\LexOffice\Api($apiKey, $httpClient); ``` -### set cache +#### Optimize your HTTP Client + +This library only prepares the `\Psr\Http\Message\RequestInterface` for the HTTP Client and returns its Response. +There are almost no error checks, no caching and no rate limiting. Your PSR-18 HTTP Client should come with a way to deal with it. + +Here is a example with `guzzlehttp/guzzle` , `kevinrob/guzzle-cache-middleware` and `spatie/guzzle-rate-limiter-middleware`: ```php -// can be any PSR-6 compatibly cache handler -// in this example we are using symfony/cache -$cacheInterface = new \Symfony\Component\Cache\Adapter\FilesystemAdapter( - 'lexoffice', - 3600, - __DIR__ . '/cache' -); +$apiKey = getenv('LEX_OFFICE_API_KEY'); + +$stack = \GuzzleHttp\HandlerStack(); +$stack->push(new \Kevinrob\GuzzleCache\CacheMiddleware\CacheMiddleware(), 'cache'); +$stack->push(\Spatie\GuzzleRateLimiterMiddleware\RateLimiterMiddleware\RateLimiterMiddleware::perSecond(2)); + +$httpClient = \GuzzleHttp\Client(['handler' => $stack]); +$api = new \Sysix\LexOffice\Api($apiKey, $httpClient); -$api->setCacheInterface($cacheInterface); ``` ### Contact Endpoint ```php -// get a page -/** @var \Clicksports\LexOffice\Api $api */ +/** @var \Sysix\LexOffice\Api $api */ $client = $api->contact(); +// filters $client->size = 100; -$client->sortDirection = 'ASC'; -$client->sortProperty = 'name'; +$client->number = 123456; +$client->customer = true; +$client->vendor = false; // get a page $response = $client->getPage(0); -//get all -$response = $client->getAll(); - // other methods $response = $client->get($entityId); $response = $client->create($data); @@ -70,7 +81,7 @@ $response = $api->country()->getAll(); ### Invoices Endpoint ```php -$response = $api->invoice()->getAll(); +$voucherList = $api->invoice()->getVoucherListClient(); // see VoucherlistClient Documentation $response = $api->invoice()->get($entityId); $response = $api->invoice()->create($data); $response = $api->invoice()->document($entityId); // get document ID @@ -79,7 +90,7 @@ $response = $api->invoice()->document($entityId, true); // get file content ### Down Payment Invoices Endpoint ```php -$response = $api->downPaymentInvoice()->getAll(); +$voucherList = $api->downPaymentInvoice()->getVoucherListClient(); // see VoucherlistClient Documentation $response = $api->downPaymentInvoice()->get($entityId); $response = $api->downPaymentInvoice()->create($data); $response = $api->downPaymentInvoice()->document($entityId); // get document ID @@ -88,7 +99,7 @@ $response = $api->downPaymentInvoice()->document($entityId, true); // get file c ### Order Confirmation Endpoint ```php -$response = $api->orderConfirmation()->getAll(); +$voucherList = $api->orderConfirmation()->getVoucherListClient(); // see VoucherlistClient Documentation $response = $api->orderConfirmation()->get($entityId); $response = $api->orderConfirmation()->create($data); $response = $api->orderConfirmation()->document($entityId); // get document ID @@ -97,7 +108,7 @@ $response = $api->orderConfirmation()->document($entityId, true); // get file co ### Quotation Endpoint ```php -$response = $api->quotation()->getAll(); +$voucherList = $api->quotation()->getVoucherListClient(); // see VoucherlistClient Documentation $response = $api->quotation()->get($entityId); $response = $api->quotation()->create($data); $response = $api->quotation()->document($entityId); // get document ID @@ -106,18 +117,18 @@ $response = $api->quotation()->document($entityId, true); // get file content ### Voucher Endpoint ```php -$response = $api->voucher()->getAll(); $response = $api->voucher()->get($entityId); $response = $api->voucher()->create($data); $response = $api->voucher()->update($entityId, $data); $response = $api->voucher()->document($entityId); // get document ID $response = $api->voucher()->document($entityId, true); // get file content +$response = $api->voucher()->upload($entitiyId, $filepath); ``` ### Credit Notes Endpoint ```php -$response = $api->creditNote()->getAll(); +$voucherList = $api->creditNote()->getVoucherListClient(); // see VoucherlistClient Documentation $response = $api->creditNote()->get($entityId); $response = $api->creditNote()->create($data); $response = $api->creditNote()->document($entityId); // get document ID @@ -147,19 +158,18 @@ $response = $api->profile()->get(); ### Recurring Templates Endpoint ```php -// get single entitiy -$response = $api->recurringTemplate()->get($entityId); - -// use pagination $client = $api->recurringTemplate(); -$client->size = 100; +// filters +$client->size = 100; +$client->sortDirection = 'DESC'; +$client->sortColumn = 'updatedDate'; // get a page $response = $client->getPage(0); -//get all -$response = $client->getAll(); +// other methods +$response = $api->recurringTemplate()->get($entityId); ``` @@ -170,6 +180,8 @@ $client = $api->voucherlist(); $client->size = 100; $client->sortDirection = 'DESC'; $client->sortColumn = 'voucherNumber'; + +// filters required $client->types = [ 'salesinvoice', 'salescreditnote', @@ -192,14 +204,18 @@ $client->statuses = [ 'rejected' ]; -// get everything what we can, not recommend: -//$client->setToEverything() +// filters optional +$client->archived = true; +$client->contactId = 'some-uuid-string'; +$client->voucherDateFrom = new \DateTime('2023-12-01'); +$client->voucherDateTo = new \DateTime('2023-12-01'); +$client->createdDateFrom = new \DateTime('2023-12-01');; +$client->createdDateTo = new \DateTime('2023-12-01'); +$client->updatedDateFrom = new \DateTime('2023-12-01'); +$client->updatedDateTo = new \DateTime('2023-12-01'); // get a page $response = $client->getPage(0); - -//get all -$response = $client->getAll(); ``` ### File Endpoint @@ -209,18 +225,9 @@ $response = $api->file()->get($entityId); ``` -### get JSON from Response +### get JSON from Success and Error Response ```php -$json = $api->*()->getAsJson($response); -``` - -### get JSON from Error Response - -```php -try { - $api->*->*(); -} catch(\Clicksports\LexOffice\Exceptions\LexOfficeApiException $exception) { - $json = $api->*()->getAsJson($exception->getPrevious()->getResponse()); -} -``` +// can be possible null because the response body can be empty +$json = \Sysix\LexOffice\Utils::getJsonFromResponse($response); +``` \ No newline at end of file diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..e64b367 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,109 @@ +# Upgrade from Version 0.x to 1.0 + +## Namespace Changed + +All Classses with started with `\Clicksports\Lexoffice` are now under `\Sysix\Lexoffice`. + +## Clients Namespace changed + +In Version `0.x` all Clients had a separate folder/namespace. Now they will all use the namespace +`\Sysix\Lexoffice\Clients` + +| Old Class | New Class | +| --- | --- | +| `new \Clicksports\Lexoffice\*\Client()` | `new \Sysix\Lexoffice\Clients\*()` | +| Examples | +| `new \Clicksports\Lexoffice\Country\Client()` | `new \Sysix\Lexoffice\Clients\Country()` | +| `new \Clicksports\Lexoffice\Voucher\Client()` | `new \Sysix\Lexoffice\Clients\Voucher()` | + +## Requires PHP 8.1 + +PHP 8.0 is now [End of Security Support](https://www.php.net/supported-versions.php) and our dev Packages already require PHP 8.1. +So we are dumping our software to PHP 8.1. + + +## ClientInterface is required + +In Version `0.x` the `\Sysix\LexOffice\Api` requires only one constructor parameter (The API Key). Now a second parameter is also required. +In the past a `GuzzleHttp\Client` was the second optional parameter. no any PSR-18 compatible HTTP-Client is allowed. + `GuzzleHttp\Client` was already one of it. + + If you want still use `guzzlehttp/guzzle` just update your code as following: + + ```php + $api = new \Sysix\LexOffice\Api($apiKey, new \GuzzleHttp\Client()); + ``` + +## Error Responses dont throws Exceptions + +To be [PSR-18 compatible](https://www.php-fig.org/psr/psr-18/) any responses with status code > 400 will not throw an Exception. +Make sure you check the status code before accessing the entities: + +```php +/** @var \Sysix\LexOffice\Api $api */ +$client = $api->*(); +$response = $client->*(); + +// https://developers.lexoffice.io/docs/#http-status-codes +if ($response->getStatusCode() === 200 /* 201 | 202 | 204 */ ) { + $json = $client()->getAsJson($response); +} +``` + +## Errors dont get wrapped into `\Sysix\Lexoffice\Exceptions\LexOfficeApiException` + +Because we are allowing [PSR-18 Clients](https://www.php-fig.org/psr/psr-18/) we don't wrap the `GuzzleException` into an `LexOfficeApiException`. +The Only times `LexOfficeApiException` is now thrown, is when you are uploading a file. +This can maybe change in the future! + +## Cache removed + +The HTTP-Client is now in charge of caching. When you are still using `guzzlehttp/guzzle`, +you can use the following middleware: [guzzle-cache-middleware](https://github.com/Kevinrob/guzzle-cache-middleware). +And implement it with: +`$api = new \Sysix\LexOffice\Api($apiKey, $guzzleClient);` + +## Clients Methods which would throw an BadMethodException + +We implemented in the `0.x` Version some methods for the future of lexoffice API. +At the moment, it doesn't look like the endpoint will be added soon. So we will remove them. + +## Functions Removed + +- `$api->setCacheInterface()` +- `$api->getCacheResponse()` +- `$api->setCacheResponse()` + +## Exceptions Removed + +- `\Sysix\Lexoffice\Exception\CacheException` +- `\Sysix\Lexoffice\Exception\BadMethodException` + + +## Functions Deprecated + +This functions will be removed in the next major (2.0) Update + +|Method Deprecated|Usage instead| +|---|---| +|`\Sysix\Lexoffice\Clients\CreditNote::getAll`|`\Sysix\Lexoffice\Clients\CreditNote::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\DownPaymentInvoice::getAll`|`\Sysix\Lexoffice\Clients\DownPaymentInvoice::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Invoice::getAll`|`\Sysix\Lexoffice\Clients\Invoice::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\OrderConfirmation::getAll`|`\Sysix\Lexoffice\Clients\OrderConfirmation::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Quotation::getAll`|`\Sysix\Lexoffice\Clients\Quotation::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Voucher::getAll`|`\Sysix\Lexoffice\Clients\Voucher::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\VoucherList::setToEverything`|not replacement| +|`\Sysix\Lexoffice\Clients\CreditNote::getPage`|`\Sysix\Lexoffice\Clients\CreditNote::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\DownPaymentInvoice::getPage`|`\Sysix\Lexoffice\Clients\DownPaymentInvoice::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Invoice::getPage`|`\Sysix\Lexoffice\Clients\Invoice::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\OrderConfirmation::getPage`|`\Sysix\Lexoffice\Clients\OrderConfirmation::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Quotation::getPage`|`\Sysix\Lexoffice\Clients\Quotation::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\Voucher::getPage`|`\Sysix\Lexoffice\Clients\Voucher::getVoucherListClient`| +|`\Sysix\Lexoffice\Clients\**::getAsJson`|`\Sysix\Lexoffice\Utils::getJsonFromResponse`| + +! You need to set a non-empty `statuses` property to the returned `\Sysix\LexOffice\Api\Clients\VoucherList`. + + +## Strict Typed + +Every Method has now strict Parameters and strict Return Types. If you extended some classes, you probably need to update them too. \ No newline at end of file diff --git a/composer.json b/composer.json index 4eec61a..8055bc5 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,11 @@ { - "name": "clicksports/lex-office-api", + "name": "sysix/lex-office-api", "type": "library", - "description": "Simple API Integration for Lex-Office", + "description": "Simple API Integration for Lexoffice", + "scripts": { + "test": "phpunit", + "lint": "phpstan analyse --ansi --error-format raw" + }, "keywords": [ "lex-office", "lexoffice", @@ -10,24 +14,29 @@ ], "license": "MIT", "require": { - "php": "^8", + "php": "^8.1", "ext-json": "*", - "ext-curl": "*", - "guzzlehttp/guzzle": "^6.2 | ^7.0", - "psr/cache": "^1.0 | ^2.0 | ^3.0" + "psr/http-client": "^1.0", + "guzzlehttp/psr7": "^2.0" }, "require-dev": { - "phpunit/phpunit": "^9.0", - "symfony/cache": "^5.1" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.4", + "guzzlehttp/guzzle": "^7.8" + }, + "suggest": { + "guzzlehttp/guzzle": "^7.0", + "kevinrob/guzzle-cache-middleware": "^5.0", + "spatie/guzzle-rate-limiter-middleware": "^2.0" }, "autoload": { "psr-4": { - "Clicksports\\LexOffice\\": "src/" + "Sysix\\LexOffice\\": "src/" } }, "autoload-dev": { "psr-4": { - "Clicksports\\LexOffice\\Tests\\": "tests/" + "Sysix\\LexOffice\\Tests\\": "tests/" } }, "minimum-stability": "dev", diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 8bd2bfd..0000000 --- a/composer.lock +++ /dev/null @@ -1,2998 +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": "6126534cf75c1f7f18f4d1e2e05f2ba3", - "packages": [ - { - "name": "guzzlehttp/guzzle", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:20:53+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:11:55+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:13:57+00:00" - }, - { - "name": "psr/cache", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", - "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/2.0.0" - }, - "time": "2021-02-03T23:23:37+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" - }, - "time": "2023-04-10T20:10:41+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "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": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" - } - ], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "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": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" - }, - "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": "2022-12-30T00:23:10+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "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.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-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/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.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": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-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 provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "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": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-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": "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/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.6.13", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1 || ^2", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.6-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "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", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.13" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-09-19T05:39:22+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "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/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "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", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "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", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-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/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-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 for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "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" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-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/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.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/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "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", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-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/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "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/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-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": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "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/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "symfony/cache", - "version": "v5.4.31", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "9c0a3a5d0718e51ff81e0605be38fe1acbee9eeb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/9c0a3a5d0718e51ff81e0605be38fe1acbee9eeb", - "reference": "9c0a3a5d0718e51ff81e0605be38fe1acbee9eeb", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0", - "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^1.1.7|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "conflict": { - "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/var-dumper": "<4.4" - }, - "provide": { - "psr/cache-implementation": "1.0|2.0", - "psr/simple-cache-implementation": "1.0|2.0", - "symfony/cache-implementation": "1.0|2.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/cache": "^1.6|^2.0", - "doctrine/dbal": "^2.13.1|^3|^4", - "predis/predis": "^1.1", - "psr/simple-cache": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Cache\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides extended PSR-6, PSR-16 (and tags) implementations", - "homepage": "https://symfony.com", - "keywords": [ - "caching", - "psr6" - ], - "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.31" - }, - "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": "2023-11-06T17:37:55+00:00" - }, - { - "name": "symfony/cache-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0|^3.0" - }, - "suggest": { - "symfony/cache-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Cache\\": "" - } - }, - "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": "Generic abstractions related to caching", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2" - }, - "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": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "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.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.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": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.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": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^2.0" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.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": "2023-07-30T20:28:31+00:00" - }, - { - "name": "symfony/var-exporter", - "version": "v6.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "374d289c13cb989027274c86206ddc63b16a2441" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/374d289c13cb989027274c86206ddc63b16a2441", - "reference": "374d289c13cb989027274c86206ddc63b16a2441", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Allows exporting any serializable PHP data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "lazy-loading", - "proxy", - "serialize" - ], - "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.3.6" - }, - "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": "2023-10-13T09:16:49+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2023-11-20T00:12:19+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "^8", - "ext-json": "*", - "ext-curl": "*" - }, - "platform-dev": [], - "plugin-api-version": "2.6.0" -} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..d461cdd --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 9 + paths: + - src + - tests \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 3135c50..fe4d9ef 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,22 @@ - - - - tests - - - \ No newline at end of file + + + + tests + + + + + + src + + + diff --git a/src/Api.php b/src/Api.php index 15431f9..a52ff7b 100644 --- a/src/Api.php +++ b/src/Api.php @@ -1,107 +1,60 @@ -apiKey = $apiKey; - $this->client = $client; } /** - * @param $method - * @param $resource - * @param array $headers - * @return $this + * @param string[] $headers */ - public function newRequest($method, $resource, $headers = []): self + public function newRequest(string $method, string $resource, array $headers = []): self { $this->setRequest( - new Request($method, $this->createApiUrl($resource), $headers) + new Request($method, $this->createApiUri($resource), $headers) ); return $this; } - /** - * @param RequestInterface $request - * @return $this - */ - public function setRequest(RequestInterface $request) + public function setRequest(RequestInterface $request): self { $request = $request ->withHeader('Authorization', 'Bearer ' . $this->apiKey) @@ -117,173 +70,93 @@ public function setRequest(RequestInterface $request) return $this; } - /** - * @param string $resource - * @return string - */ - protected function createApiUrl(string $resource): string + protected function createApiUri(string $resource): UriInterface { - return $this->apiUrl . '/' . $this->apiVersion . '/' . $resource; + return new Uri($this->apiUrl . '/' . $this->apiVersion . '/' . $resource); } - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getResponse() + public function getResponse(): ResponseInterface { - $cache = null; - if ($this->cacheInterface) { - $response = $cache = $this->getCacheResponse($this->request); - } - - // when no cacheInterface is set or the cache is invalid - if (!isset($response) || !$response) { - try { - $response = $this->client->send($this->request); - } catch (GuzzleException $exception) { - throw new LexOfficeApiException( - $exception->getMessage(), - $exception->getCode(), - $exception - ); - } - } - - // set cache response when cache is invalid - if ($this->cacheInterface && !$cache) { - $this->setCacheResponse($this->request, $response); - } - - return $response; + return $this->client->sendRequest($this->request); } - /** - * @return ContactClient - */ - public function contact() + public function contact(): Contact { - return new ContactClient($this); + return new Contact($this); } - /** - * @return CountryClient - */ - public function country() + public function country(): Country { - return new CountryClient($this); + return new Country($this); } - /** - * @return EventClient - */ - public function event() + public function event(): Event { - return new EventClient($this); + return new Event($this); } - /** - * @return InvoiceClient - */ - public function invoice() + public function invoice(): Invoice { - return new InvoiceClient($this); + return new Invoice($this); } - /** - * @return DownPaymentInvoiceClient - */ - public function downPaymentInvoice() + public function downPaymentInvoice(): DownPaymentInvoice { - return new DownPaymentInvoiceClient($this); + return new DownPaymentInvoice($this); } - /** - * @return OrderConfirmationClient - */ - public function orderConfirmation() + public function orderConfirmation(): OrderConfirmation { - return new OrderConfirmationClient($this); + return new OrderConfirmation($this); } - /** - * @return PaymentClient - */ - public function payment() + public function payment(): Payment { - return new PaymentClient($this); + return new Payment($this); } - /** - * @return PaymentConditionClient - */ - public function paymentCondition() + public function paymentCondition(): PaymentCondition { - return new PaymentConditionClient($this); + return new PaymentCondition($this); } - /** - * @return CreditNoteClient - */ - public function creditNote() + public function creditNote(): CreditNote { - return new CreditNoteClient($this); + return new CreditNote($this); } - /** - * @return QuotationClient - */ - public function quotation() + public function quotation(): Quotation { - return new QuotationClient($this); + return new Quotation($this); } - /** - * @return VoucherClient - */ - public function voucher() + public function voucher(): Voucher { - return new VoucherClient($this); + return new Voucher($this); } - /** - * @return RecurringTemplateClient - */ - public function recurringTemplate() + public function recurringTemplate(): RecurringTemplate { - return new RecurringTemplateClient($this); + return new RecurringTemplate($this); } - /** - * @return VoucherlistClient - */ - public function voucherlist() + public function voucherlist(): VoucherList { - return new VoucherlistClient($this); + return new VoucherList($this); } - /** - * @return ProfileClient - */ - public function profile() + public function profile(): Profile { - return new ProfileClient($this); + return new Profile($this); } - /** - * @return PostingCategoryClient - */ - public function postingCategory() + public function postingCategory(): PostingCategory { - return new PostingCategoryClient($this); + return new PostingCategory($this); } - /** - * @return FileClient - */ - public function file() + public function file(): File { - return new FileClient($this); + return new File($this); } } diff --git a/src/BaseClient.php b/src/BaseClient.php index d3d5ce9..f68b2ab 100644 --- a/src/BaseClient.php +++ b/src/BaseClient.php @@ -1,102 +1,29 @@ -api = $lexOffice; } /** - * @param array $data - * @return ResponseInterface - * @throws Exceptions\CacheException - * @throws Exceptions\LexOfficeApiException + * @deprecated 1.0 use Sysix\LexOffice\Utils::getJsonFromResponse() */ - public function create(array $data) + public function getAsJson(ResponseInterface $response): mixed { - $api = $this->api->newRequest('POST', $this->resource); - - $api->request = $api->request->withBody($this->createStream($data)); - - return $api->getResponse(); - } - - /** - * @param string $id - * @param array $data - * @return void - * @throws Exception - */ - public function update(string $id, array $data) - { - throw new Exceptions\BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @param string $id - * @return ResponseInterface - * @throws Exceptions\CacheException - * @throws Exceptions\LexOfficeApiException - */ - public function get(string $id) - { - return $this->api->newRequest('GET', $this->resource . '/' . $id) - ->getResponse(); - } + trigger_error(self::class . '::' . __METHOD__ . ' should not be called anymore, use \Sysix\LexOffice\Utils::getJsonFromResponse instead', E_USER_DEPRECATED); - /** - * @param ResponseInterface $response - * @return object - */ - public function getAsJson(ResponseInterface $response) - { $body = $response->getBody()->__toString(); return Utils::jsonDecode($body); } - - /** - * @param mixed $content - * @return StreamInterface - */ - protected function createStream($content): StreamInterface - { - return Utils::streamFor( - Utils::jsonEncode($content) - ); - } - - /** - * @param array $content - * @param string|null $boundary - * @return MultipartStream - */ - protected function createMultipartStream(array $content, string $boundary = null): MultipartStream - { - $stream = []; - $boundary = $boundary ?: '--lexoffice'; - - foreach ($content as $key => $value) { - $stream[] = [ - 'name' => $key, - 'contents' => $value - ]; - } - - return new MultipartStream($stream, $boundary); - } } diff --git a/src/ClientInterface.php b/src/ClientInterface.php index f207cd4..ba9de6a 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -1,42 +1,8 @@ -number; + $params['customer'] = $this->customer; + $params['vendor'] = $this->vendor; + + return parent::buildQueryParams($params); + } +} \ No newline at end of file diff --git a/src/Clients/Country.php b/src/Clients/Country.php new file mode 100644 index 0000000..6c7b32d --- /dev/null +++ b/src/Clients/Country.php @@ -0,0 +1,17 @@ +api->newRequest('GET', $this->resource) + ->getResponse(); + } +} diff --git a/src/Clients/CreditNote.php b/src/Clients/CreditNote.php new file mode 100644 index 0000000..2045905 --- /dev/null +++ b/src/Clients/CreditNote.php @@ -0,0 +1,22 @@ +api->newRequest('GET', $this->resource) + ->getResponse(); + } +} diff --git a/src/Clients/File.php b/src/Clients/File.php new file mode 100644 index 0000000..482f3bd --- /dev/null +++ b/src/Clients/File.php @@ -0,0 +1,43 @@ +validateFileFromFilePath($filepath); + + $body = Utils::createMultipartStream([ + 'file' => fopen($filepath, 'r'), + 'type' => $type + ]); + + $api = $this->api->newRequest('POST', $this->resource, [ + 'Content-Type' => 'multipart/form-data; boundary=' . $body->getBoundary() + ]); + + $api->request = $api->request->withBody($body); + + return $api->getResponse(); + } +} diff --git a/src/Clients/Invoice.php b/src/Clients/Invoice.php new file mode 100644 index 0000000..c29a789 --- /dev/null +++ b/src/Clients/Invoice.php @@ -0,0 +1,22 @@ +api->newRequest('GET', $this->resource) + ->getResponse(); + } +} diff --git a/src/Clients/PostingCategory.php b/src/Clients/PostingCategory.php new file mode 100644 index 0000000..d5cedc4 --- /dev/null +++ b/src/Clients/PostingCategory.php @@ -0,0 +1,17 @@ +api->newRequest('GET', $this->resource) + ->getResponse(); + } +} diff --git a/src/Clients/Profile.php b/src/Clients/Profile.php new file mode 100644 index 0000000..707bb25 --- /dev/null +++ b/src/Clients/Profile.php @@ -0,0 +1,18 @@ +api->newRequest('GET', $this->resource) + ->getResponse(); + } +} diff --git a/src/Clients/Quotation.php b/src/Clients/Quotation.php new file mode 100644 index 0000000..6fdd832 --- /dev/null +++ b/src/Clients/Quotation.php @@ -0,0 +1,22 @@ +api->newRequest('POST', $this->resource . ($finalized ? '?finalize=true' : '')); + + $api->request = $api->request->withBody(Utils::createStream($data)); + + return $api->getResponse(); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/CreateTrait.php b/src/Clients/Traits/CreateTrait.php new file mode 100644 index 0000000..b8b6edc --- /dev/null +++ b/src/Clients/Traits/CreateTrait.php @@ -0,0 +1,21 @@ +api->newRequest('POST', $this->resource); + + $api->request = $api->request->withBody(Utils::createStream($data)); + + return $api->getResponse(); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/DeleteTrait.php b/src/Clients/Traits/DeleteTrait.php new file mode 100644 index 0000000..db1f3a1 --- /dev/null +++ b/src/Clients/Traits/DeleteTrait.php @@ -0,0 +1,15 @@ +api->newRequest('DELETE', $this->resource . '/' . rawurlencode($id)) + ->getResponse(); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/DocumentClientTrait.php b/src/Clients/Traits/DocumentClientTrait.php new file mode 100644 index 0000000..1e675fe --- /dev/null +++ b/src/Clients/Traits/DocumentClientTrait.php @@ -0,0 +1,37 @@ +api + ->newRequest('GET', $this->resource . '/' . rawurlencode($id) . '/document') + ->getResponse(); + + if ($asContent === false) { + return $response; + } + + if ($response->getStatusCode() !== 200) { + return $response; + } + + /** @var ?stdClass{documentField: string} $content */ + $content = Utils::getJsonFromResponse($response); + + if ($content === null) { + return $response; + } + + $fileClient = new File($this->api); + + return $fileClient->get($content->documentFileId); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/GetTrait.php b/src/Clients/Traits/GetTrait.php new file mode 100644 index 0000000..026db7a --- /dev/null +++ b/src/Clients/Traits/GetTrait.php @@ -0,0 +1,14 @@ +api->newRequest('GET', $this->resource . '/' . rawurlencode($id)) + ->getResponse(); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/UpdateTrait.php b/src/Clients/Traits/UpdateTrait.php new file mode 100644 index 0000000..4544ad1 --- /dev/null +++ b/src/Clients/Traits/UpdateTrait.php @@ -0,0 +1,21 @@ +api->newRequest('PUT', $this->resource . '/' . rawurlencode($id)); + + $api->request = $api->request->withBody(Utils::createStream($data)); + + return $api->getResponse(); + } +} \ No newline at end of file diff --git a/src/Clients/Traits/VoucherListTrait.php b/src/Clients/Traits/VoucherListTrait.php new file mode 100644 index 0000000..24eaf3a --- /dev/null +++ b/src/Clients/Traits/VoucherListTrait.php @@ -0,0 +1,45 @@ +api); + $client->setToEverything(); + $client->types = $this->voucherListTypes; + + return $client->getPage($page); + } + + /** + * @deprecated 1.0 Not recommend anymore because of Rate Limiting, WILL be removed in 2.0 + */ + public function getAll(): ResponseInterface + { + trigger_error(self::class . '::' . __METHOD__ . ' should not be called anymore, in future versions this method WILL not exist', E_USER_DEPRECATED); + + $client = new VoucherList($this->api); + $client->setToEverything(); + $client->types = $this->voucherListTypes; + + return $client->getAll(); + } + + public function getVoucherListClient(): VoucherList + { + $client = new VoucherList($this->api); + $client->types = $this->voucherListTypes; + + return $client; + } +} \ No newline at end of file diff --git a/src/Clients/Voucher.php b/src/Clients/Voucher.php new file mode 100644 index 0000000..3f95e4f --- /dev/null +++ b/src/Clients/Voucher.php @@ -0,0 +1,60 @@ +validateFileFromFilePath($filepath); + + $body = Utils::createMultipartStream([ + 'file' => fopen($filepath, 'r') + ]); + + $api = $this->api->newRequest('POST', $this->resource . '/' . rawurlencode($id), [ + 'Content-Type' => 'multipart/form-data; boundary=' . $body->getBoundary() + ]); + + $api->request = $api->request->withBody($body); + + return $api->getResponse(); + } + + /** + * @deprecated 1.0 Not recommend anymore because of Rate Limiting, WILL be removed in 2.0 + */ + public function getAll(): ResponseInterface + { + trigger_error(self::class . '::' . __METHOD__ . ' should not be called anymore, in future versions this method WILL not exist', E_USER_DEPRECATED); + + $client = new VoucherList($this->api); + + /** + * @link https://developers.lexoffice.io/docs/#vouchers-endpoint-purpose + */ + $client->statuses = ['open', 'paid', 'paidoff', 'voided', 'transferred', 'sepadebit']; + $client->types = ['salesinvoice', 'salescreditnote', 'purchaseinvoice', 'purchasecreditnote']; + + return $client->getAll(); + } +} diff --git a/src/Clients/VoucherList.php b/src/Clients/VoucherList.php new file mode 100644 index 0000000..4e1f332 --- /dev/null +++ b/src/Clients/VoucherList.php @@ -0,0 +1,85 @@ +types = [ + 'salesinvoice', + 'salescreditnote', + 'purchaseinvoice', + 'purchasecreditnote', + 'invoice', + 'downpaymentinvoice', + 'creditnote', + 'orderconfirmation', + 'quotation' + ]; + + $this->statuses = [ + 'draft', + 'open', + 'paid', + 'paidoff', + 'voided', + //'overdue', + 'accepted', + 'rejected' + ]; + + return $this; + } + + protected function buildQueryParams(array $params): string + { + $dateFormat = DateTimeInterface::ATOM; + + $params['voucherType'] = implode(',', $this->types); + $params['voucherStatus'] = implode(',', $this->statuses); + $params['archived'] = $this->archived; + $params['voucherDateFrom'] = $this->voucherDateFrom?->format($dateFormat); + $params['voucherDateTo'] = $this->voucherDateTo?->format($dateFormat); + $params['createdDateFrom'] = $this->createdDateFrom?->format($dateFormat); + $params['createdDateTo'] = $this->createdDateTo?->format($dateFormat); + $params['updatedDateFrom'] = $this->updatedDateFrom?->format($dateFormat); + $params['updatedDateTo'] = $this->updatedDateTo?->format($dateFormat); + + return parent::buildQueryParams($params); + } +} diff --git a/src/Config/FileClient/VoucherConfig.php b/src/Config/FileClient/VoucherConfig.php new file mode 100644 index 0000000..e48384f --- /dev/null +++ b/src/Config/FileClient/VoucherConfig.php @@ -0,0 +1,31 @@ +supportedExtension) . ')/'; + $matchResult = preg_match($regex, $filepath, $matches); + + if (!$matchResult || !$matches[0]) { + throw new LexOfficeApiException('file extension is not supported: ' . basename($filepath) . ' '); + } + + if (!is_file($filepath)) { + throw new LexOfficeApiException('file could not be found to upload: ' . $filepath); + } + + if (filesize($filepath) > self::MAX_FILE_SIZE) { + throw new LexOfficeApiException('file is to big to upload: ' . $filepath . ', max upload size: ' . self::MAX_FILE_SIZE . 'bytes'); + } + } +} \ No newline at end of file diff --git a/src/Config/FileClientConfig.php b/src/Config/FileClientConfig.php new file mode 100644 index 0000000..3e2c873 --- /dev/null +++ b/src/Config/FileClientConfig.php @@ -0,0 +1,13 @@ +sortDirection . - '&property=' .$this->sortProperty; - } - - /** - * @param string $id - * @param array $data - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function update(string $id, array $data) - { - $api = $this->api->newRequest('PUT', $this->resource . '/' . $id); - - $api->request = $api->request->withBody($this->createStream($data)); - - return $api->getResponse(); - } -} diff --git a/src/Country/Client.php b/src/Country/Client.php deleted file mode 100644 index 854ef35..0000000 --- a/src/Country/Client.php +++ /dev/null @@ -1,54 +0,0 @@ -resource); - } - - /** - * @param string $id - * @throws BadMethodCallException - */ - public function get(string $id) - { - throw new BadMethodCallException('method get is not defined for ' . $this->resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - return $this->api->newRequest('GET', $this->resource) - ->getResponse(); - } -} diff --git a/src/CreditNote/Client.php b/src/CreditNote/Client.php deleted file mode 100644 index 6b62fc7..0000000 --- a/src/CreditNote/Client.php +++ /dev/null @@ -1,50 +0,0 @@ -resource; - - $this->resource .= $finalized ? '?finalize=true' : ''; - $response = parent::create($data); - $this->resource = $oldResource; - - return $response; - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - $client = new VoucherlistClient($this->api); - - $client->setToEverything(); - $client->types = ['creditnote']; - - return $client->getAll(); - } -} diff --git a/src/DownPaymentInvoice/Client.php b/src/DownPaymentInvoice/Client.php deleted file mode 100644 index fc44576..0000000 --- a/src/DownPaymentInvoice/Client.php +++ /dev/null @@ -1,53 +0,0 @@ -resource); - } - - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - $client = new VoucherlistClient($this->api); - - $client->setToEverything(); - $client->types = ['downpaymentinvoice']; - - return $client->getAll(); - } -} diff --git a/src/Event/Client.php b/src/Event/Client.php deleted file mode 100644 index 77e1a14..0000000 --- a/src/Event/Client.php +++ /dev/null @@ -1,53 +0,0 @@ -api->newRequest('GET', 'event-subscriptions') - ->getResponse(); - } - - /** - * @param string $id - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function delete(string $id) - { - return $this->api->newRequest('DELETE', 'event-subscriptions/' . $id) - ->getResponse(); - } -} diff --git a/src/Exceptions/BadMethodCallException.php b/src/Exceptions/BadMethodCallException.php deleted file mode 100644 index b9b0e77..0000000 --- a/src/Exceptions/BadMethodCallException.php +++ /dev/null @@ -1,8 +0,0 @@ -supportedExtension) . ')/'; - $matchResult = preg_match($regex, $filepath, $matches); - - if (!$matchResult || !$matches[0]) { - throw new LexOfficeApiException('file extension is not supported: ' . basename($filepath) . ' '); - } - - if (!is_file($filepath)) { - throw new LexOfficeApiException('file could not be found to upload: ' . $filepath); - } - - if (filesize($filepath) > self::MAX_FILE_SIZE) { - throw new LexOfficeApiException('file is to big to upload: ' . $filepath . ', max upload size: ' . self::MAX_FILE_SIZE . 'bytes'); - } - - $body = $this->createMultipartStream([ - 'file' => fopen($filepath, 'r'), - 'type' => $type - ]); - - $api = $this->api->newRequest('POST', $this->resource, [ - 'Content-Type' => 'multipart/form-data; boundary=' . $body->getBoundary() - ]); - - $api->request = $api->request->withBody($body); - - return $api->getResponse(); - } - - /** - * @param array $data - * @throws BadMethodCallException - */ - public function create(array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @throws BadMethodCallException - */ - public function getAll() - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } -} diff --git a/src/Invoice/Client.php b/src/Invoice/Client.php deleted file mode 100644 index 62c1244..0000000 --- a/src/Invoice/Client.php +++ /dev/null @@ -1,51 +0,0 @@ -resource; - - $this->resource .= $finalized ? '?finalize=true' : ''; - $response = parent::create($data); - $this->resource = $oldResource; - - return $response; - } - - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - $client = new VoucherlistClient($this->api); - - $client->setToEverything(); - $client->types = ['invoice']; - - return $client->getAll(); - } -} diff --git a/src/OrderConfirmation/Client.php b/src/OrderConfirmation/Client.php deleted file mode 100644 index 00ab2f0..0000000 --- a/src/OrderConfirmation/Client.php +++ /dev/null @@ -1,32 +0,0 @@ -api); - - $client->setToEverything(); - $client->types = ['orderconfirmation']; - - return $client->getAll(); - } -} diff --git a/src/PaginationClient.php b/src/PaginationClient.php index be5966b..53dacc1 100644 --- a/src/PaginationClient.php +++ b/src/PaginationClient.php @@ -1,56 +1,71 @@ -resource . '?page=' . $page . '&size=' . $this->size; + return $this->resource . '?' . $this->buildQueryParams([ + 'page'=> $page + ]); } /** - * @param int $page - * @return ResponseInterface - * @throws Exceptions\CacheException - * @throws Exceptions\LexOfficeApiException + * @param array $params */ - public function getPage(int $page): ResponseInterface + protected function buildQueryParams(array $params): string { - $api = $this->api->newRequest( - 'GET', - $this->generateUrl($page) - ); + $params['size'] = $this->size; - return $api->getResponse(); + // contact endpoint can't be sorted but is a Pagination client + if (isset($this->sortColumn)) { + $params['sort'] = $this->sortColumn . ',' . $this->sortDirection; + } + + return http_build_query($params); + } + + public function getPage(int $page): ResponseInterface + { + return $this->api + ->newRequest('GET', $this->generatePageUrl($page)) + ->getResponse(); } /** - * @return ResponseInterface - * @throws Exceptions\CacheException - * @throws Exceptions\LexOfficeApiException + * @deprecated 1.0 Not recommend anymore because of Rate Limiting, WILL be removed in 2.0 */ - public function getAll() + public function getAll(): ResponseInterface { + trigger_error(self::class . '::' . __METHOD__ . ' should not be called anymore, in future versions this method WILL not exist', E_USER_DEPRECATED); + $response = $this->getPage(0); - $result = $this->getAsJson($response); + /** @var ?stdClass{totalPages:int, content:stdClass[]} $result */ + $result = Utils::getJsonFromResponse($response); - if ($result->totalPages == 1) { + if ($result === null || $result->totalPages == 1) { return $response; } // update content to get all contacts for ($i = 1; $i < $result->totalPages; $i++) { $responsePage = $this->getPage($i); - $resultPage = $this->getAsJson($responsePage); + /** @var ?stdClass{totalPages:int, content:stdClass[]} $resultPage */ + $resultPage = Utils::getJsonFromResponse($responsePage); + + if ($resultPage === null) { + continue; + } foreach ($resultPage->content as $entity) { $result->content = [ @@ -60,6 +75,6 @@ public function getAll() } } - return $response->withBody($this->createStream($result)); + return $response->withBody(Utils::createStream($result)); } } \ No newline at end of file diff --git a/src/Payment/Client.php b/src/Payment/Client.php deleted file mode 100644 index dca0b34..0000000 --- a/src/Payment/Client.php +++ /dev/null @@ -1,43 +0,0 @@ -resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @throws BadMethodCallException - */ - public function getAll() - { - throw new BadMethodCallException('method getAll is not defined for ' . $this->resource); - } -} diff --git a/src/PaymentCondition/Client.php b/src/PaymentCondition/Client.php deleted file mode 100644 index 46d9a7b..0000000 --- a/src/PaymentCondition/Client.php +++ /dev/null @@ -1,54 +0,0 @@ -resource); - } - - /** - * @param string $id - * @throws BadMethodCallException - */ - public function get(string $id) - { - throw new BadMethodCallException('method get is not defined for ' . $this->resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - return $this->api->newRequest('GET', $this->resource) - ->getResponse(); - } -} diff --git a/src/PostingCategory/Client.php b/src/PostingCategory/Client.php deleted file mode 100644 index 0b4cd7c..0000000 --- a/src/PostingCategory/Client.php +++ /dev/null @@ -1,54 +0,0 @@ -resource); - } - - /** - * @param array $data - * @throws BadMethodCallException - */ - public function create(array $data) - { - throw new BadMethodCallException('method create is not defined for ' . $this->resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - return $this->api->newRequest('GET', $this->resource) - ->getResponse(); - } -} diff --git a/src/Profile/Client.php b/src/Profile/Client.php deleted file mode 100644 index 3e78ad4..0000000 --- a/src/Profile/Client.php +++ /dev/null @@ -1,54 +0,0 @@ -api->newRequest('GET', $this->resource) - ->getResponse(); - } - /** - * @throws BadMethodCallException - */ - public function getAll() - { - throw new BadMethodCallException('method getAll is not defined for ' . $this->resource); - } - - /** - * @param array $data - * @throws BadMethodCallException - */ - public function create(array $data) - { - throw new BadMethodCallException('method create is not defined for ' . $this->resource); - } - - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } -} diff --git a/src/Quotation/Client.php b/src/Quotation/Client.php deleted file mode 100644 index 29bb3af..0000000 --- a/src/Quotation/Client.php +++ /dev/null @@ -1,50 +0,0 @@ -resource; - - $this->resource .= $finalized ? '?finalize=true' : ''; - $response = parent::create($data); - $this->resource = $oldResource; - - return $response; - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - $client = new VoucherlistClient($this->api); - - $client->setToEverything(); - $client->types = ['quotation']; - - return $client->getAll(); - } -} diff --git a/src/RecurringTemplate/Client.php b/src/RecurringTemplate/Client.php deleted file mode 100644 index 1d8f21f..0000000 --- a/src/RecurringTemplate/Client.php +++ /dev/null @@ -1,31 +0,0 @@ -resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not defined for ' . $this->resource); - } -} diff --git a/src/Traits/CacheResponseTrait.php b/src/Traits/CacheResponseTrait.php deleted file mode 100644 index 8eef40c..0000000 --- a/src/Traits/CacheResponseTrait.php +++ /dev/null @@ -1,118 +0,0 @@ -cacheInterface = $cache; - - return $this; - } - - /** - * @param RequestInterface $request - * @return string - */ - protected function getCacheName(RequestInterface $request): string - { - return 'lex-office-' . str_replace('/', '-', $request->getUri()->getPath()) . $request->getUri()->getQuery(); - } - - - /** - * @param RequestInterface $request - * @return Response|null - * @throws CacheException - */ - public function getCacheResponse(RequestInterface $request): ?Response - { - $cacheName = $this->getCacheName($request); - - try { - if ($request->getMethod() == 'GET') { - $cache = $this->cacheInterface->getItem($cacheName); - - if ($cache && $cache->isHit()) { - $cache = Utils::jsonDecode($cache->get()); - - return new Response( - $cache->status, - (array)$cache->headers, - Utils::streamFor($cache->body), - $cache->version, - $cache->reason - ); - } - } - } catch (InvalidArgumentException $exception) { - throw new CacheException( - 'could not load cache response from request' . $request->getUri()->getPath(), - $exception->getCode(), - $exception - ); - } - - return null; - } - - /** - * @param RequestInterface $request - * @param ResponseInterface $response - * @return $this - * @throws CacheException - */ - public function setCacheResponse(RequestInterface $request, ResponseInterface $response): self - { - if ($request->getMethod() != 'GET') { - return $this; - } - - if (!$this->cacheInterface) { - throw new CacheException('response could not be cached, cacheInterface is not defined'); - } - - try { - $cacheName = $this->getCacheName($request); - - $cache = new stdClass(); - $cache->status = $response->getStatusCode(); - $cache->headers = $response->getHeaders(); - $cache->body = $response->getBody()->__toString(); - $cache->version = $response->getProtocolVersion(); - $cache->reason = $response->getReasonPhrase(); - - $item = $this->cacheInterface->getItem($cacheName); - $item->set(Utils::jsonEncode($cache)); - - $this->cacheInterface->save($item); - } catch (InvalidArgumentException $exception) { - throw new CacheException( - 'response could not set cache for request ' . $request->getUri()->getPath(), - $exception->getCode(), - $exception - ); - } - - return $this; - } -} \ No newline at end of file diff --git a/src/Traits/DocumentClientTrait.php b/src/Traits/DocumentClientTrait.php deleted file mode 100644 index 9bc60c7..0000000 --- a/src/Traits/DocumentClientTrait.php +++ /dev/null @@ -1,33 +0,0 @@ -api->newRequest('GET', $this->resource . '/' . $id . '/document') - ->getResponse(); - - if ($asContent === false) { - return $response; - } - - $content = $this->getAsJson($response); - $fileClient = new FileClient($this->api); - - return $fileClient->get($content->documentFileId); - } -} \ No newline at end of file diff --git a/src/Utils.php b/src/Utils.php index def7c55..d1cce28 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -1,57 +1,63 @@ getBody()->__toString(); + + if ($response->getHeaderLine("Content-Type") === "application/json") { + return self::jsonDecode($body); + } + + return null; + } + /** - * @param string $resource - * @param array $options - * @return Stream + * @param array{size?: int, metadata?: mixed[], mode?: bool, seekable?: bool} $options */ - public static function streamFor($resource = '', array $options = []): Stream + public static function streamFor(string $resource = '', array $options = []): Stream { if (is_scalar($resource)) { $stream = fopen('php://temp', 'r+'); - if ($resource !== '') { + if ($resource !== '' && $stream) { fwrite($stream, $resource); fseek($stream, 0); + + return new Stream($stream, $options); } - return new Stream($stream, $options); } throw new InvalidArgumentException('Invalid resource type: ' . gettype($resource)); } /** - * @param $value - * @param int $options - * @param int $depth - * @return string + * @param int<1, max> $depth */ - public static function jsonEncode($value, int $options = 0, int $depth = 512): string + public static function jsonEncode(mixed $value, int $options = 0, int $depth = 512): string { - $json = json_encode($value, $options, $depth); + $json = (string) json_encode($value, $options, $depth); if (JSON_ERROR_NONE !== json_last_error()) { throw new InvalidArgumentException('json_encode error: ' . json_last_error_msg()); } - /** @var string */ return $json; } /** - * @param string $json - * @param bool $assoc - * @param int $depth - * @param int $options - * @return mixed + * @param int<1, max> $depth */ - public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) + public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0): mixed { $data = json_decode($json, $assoc, $depth, $options); if (JSON_ERROR_NONE !== json_last_error()) { @@ -60,4 +66,28 @@ public static function jsonDecode(string $json, bool $assoc = false, int $depth return $data; } + + + public static function createStream(mixed $content): StreamInterface + { + return Utils::streamFor(Utils::jsonEncode($content)); + } + + /** + * @param array $content + */ + public static function createMultipartStream(array $content, string $boundary = null): MultipartStream + { + $stream = []; + $boundary = $boundary ?: '--lexoffice'; + + foreach ($content as $key => $value) { + $stream[] = [ + 'name' => $key, + 'contents' => $value + ]; + } + + return new MultipartStream($stream, $boundary); + } } \ No newline at end of file diff --git a/src/Voucher/Client.php b/src/Voucher/Client.php deleted file mode 100644 index e151f07..0000000 --- a/src/Voucher/Client.php +++ /dev/null @@ -1,48 +0,0 @@ -api->newRequest('PUT', $this->resource . '/' . $id); - - $api->request = $api->request->withBody($this->createStream($data)); - - return $api->getResponse(); - } - - /** - * @return ResponseInterface - * @throws CacheException - * @throws LexOfficeApiException - */ - public function getAll() - { - $client = new VoucherlistClient($this->api); - - /** - * @link https://developers.lexoffice.io/docs/#vouchers-endpoint-purpose - */ - $client->statuses = ['open', 'paid', 'paidoff', 'voided', 'transferred', 'sepadebit']; - $client->types = ['salesinvoice', 'salescreditnote', 'purchaseinvoice', 'purchasecreditnote']; - - return $client->getAll(); - } -} diff --git a/src/Voucherlist/Client.php b/src/Voucherlist/Client.php deleted file mode 100644 index 723c3ff..0000000 --- a/src/Voucherlist/Client.php +++ /dev/null @@ -1,84 +0,0 @@ -types = [ - 'salesinvoice', - 'salescreditnote', - 'purchaseinvoice', - 'purchasecreditnote', - 'invoice', - 'downpaymentinvoice', - 'creditnote', - 'orderconfirmation', - 'quotation' - ]; - - $this->statuses = [ - 'draft', - 'open', - 'paid', - 'paidoff', - 'voided', - //'overdue', - 'accepted', - 'rejected' - ]; - } - - /** - * @param int $page - * @return string - */ - public function generateUrl(int $page): string - { - return parent::generateUrl($page) . - '&sort=' . $this->sortColumn . ',' . $this->sortDirection . - '&voucherType=' . implode(',', $this->types) . - '&voucherStatus=' . implode(',', $this->statuses); - } - - /** - * @param array $data - * @throws BadMethodCallException - */ - public function create(array $data) - { - throw new BadMethodCallException('method create is not supported for ' . $this->resource); - } - - /** - * @param string $id - * @param array $data - * @throws BadMethodCallException - */ - public function update(string $id, array $data) - { - throw new BadMethodCallException('method update is not supported for ' . $this->resource); - } - - /** - * @param string $id - * @throws BadMethodCallException - */ - public function get(string $id) - { - throw new BadMethodCallException('method get is not supported for ' . $this->resource); - } -} diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 035b545..e4a433e 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -1,89 +1,113 @@ -newRequest('GET', '/'); return $stub; } - /** @noinspection PhpFullyQualifiedNameUsageInspection */ - public function testClients() + public function testClients(): void { - $stub = $this->createApiMockObject(new Response(), [ - 'contact', - 'country', - 'event', - 'invoice', - 'downPaymentInvoice', - 'orderConfirmation', - 'quotation', - 'voucher', - 'voucherlist', - 'profile', - 'creditNote', - 'payment', - 'paymentCondition', - 'file', - 'recurringTemplate', - 'postingCategory' - ]); - - $this->assertInstanceOf(\Clicksports\LexOffice\Country\Client::class, $stub->country()); - $this->assertInstanceOf(\Clicksports\LexOffice\Contact\Client::class, $stub->contact()); - $this->assertInstanceOf(\Clicksports\LexOffice\Event\Client::class, $stub->event()); - $this->assertInstanceOf(\Clicksports\LexOffice\Invoice\Client::class, $stub->invoice()); - $this->assertInstanceOf(\Clicksports\LexOffice\DownPaymentInvoice\Client::class, $stub->downPaymentInvoice()); - $this->assertInstanceOf(\Clicksports\LexOffice\OrderConfirmation\Client::class, $stub->orderConfirmation()); - $this->assertInstanceOf(\Clicksports\LexOffice\Quotation\Client::class, $stub->quotation()); - $this->assertInstanceOf(\Clicksports\LexOffice\Voucher\Client::class, $stub->voucher()); - $this->assertInstanceOf(\Clicksports\LexOffice\Voucherlist\Client::class, $stub->voucherlist()); - $this->assertInstanceOf(\Clicksports\LexOffice\Profile\Client::class, $stub->profile()); - $this->assertInstanceOf(\Clicksports\LexOffice\CreditNote\Client::class, $stub->creditNote()); - $this->assertInstanceOf(\Clicksports\LexOffice\Payment\Client::class, $stub->payment()); - $this->assertInstanceOf(\Clicksports\LexOffice\PaymentCondition\Client::class, $stub->paymentCondition()); - $this->assertInstanceOf(\Clicksports\LexOffice\File\Client::class, $stub->file()); - $this->assertInstanceOf(\Clicksports\LexOffice\RecurringTemplate\Client::class, $stub->recurringTemplate()); - $this->assertInstanceOf(\Clicksports\LexOffice\PostingCategory\Client::class, $stub->postingCategory()); + $stub = $this->createApiMockObject(new Response()); + + $this->assertInstanceOf(Country::class, $stub->country()); + $this->assertInstanceOf(Contact::class, $stub->contact()); + $this->assertInstanceOf(Event::class, $stub->event()); + $this->assertInstanceOf(Invoice::class, $stub->invoice()); + $this->assertInstanceOf(DownPaymentInvoice::class, $stub->downPaymentInvoice()); + $this->assertInstanceOf(OrderConfirmation::class, $stub->orderConfirmation()); + $this->assertInstanceOf(Quotation::class, $stub->quotation()); + $this->assertInstanceOf(Voucher::class, $stub->voucher()); + $this->assertInstanceOf(VoucherList::class, $stub->voucherlist()); + $this->assertInstanceOf(Profile::class, $stub->profile()); + $this->assertInstanceOf(CreditNote::class, $stub->creditNote()); + $this->assertInstanceOf(Payment::class, $stub->payment()); + $this->assertInstanceOf(PaymentCondition::class, $stub->paymentCondition()); + $this->assertInstanceOf(File::class, $stub->file()); + $this->assertInstanceOf(RecurringTemplate::class, $stub->recurringTemplate()); + $this->assertInstanceOf(PostingCategory::class, $stub->postingCategory()); } - public function testApiUrl() + public function testApiUrl(): void { $stub = $this->createApiMockObject( new Response(200, [], 'post-content') ); - $this->assertStringStartsWith($stub->apiUrl, $stub->request->getUri()); + $this->assertStringStartsWith('api.lexoffice.io', $stub->request->getUri()->getHost()); $stub->apiUrl = 'https://test.de'; $stub->newRequest('POST', 'post-content'); - $this->assertStringStartsWith($stub->apiUrl, $stub->request->getUri()); + $this->assertStringStartsWith('test.de', $stub->request->getUri()->getHost()); + } + + public function testGetSuccessResponse(): void + { + $stub = $this->createApiMockObject(new Response(200, [], 'response-body')); + $response = $stub->getResponse(); + + $this->assertInstanceOf(ResponseInterface::class, $response); } - public function testGetResponse() + /** + * @link https://developers.lexoffice.io/docs/#error-codes + */ + public function testGetFailedResponse(): void { - $responseMock = new Response(200, [], 'response-body'); - $stub = $this->createApiMockObject($responseMock); + $stub = $this->createApiMockObject(new Response(401, [], '{"message":"Unauthorized"}')); + $response = $stub->getResponse(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $stub = $this->createApiMockObject(new Response(403, [], '{"message":"\'{accessToken}\' not a valid key=value pair (missing equal-sign) in Authorization header: \'Bearer {accessToken}\'."}')); + $response = $stub->getResponse(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $stub = $this->createApiMockObject(new Response(500, [], '{"message":null}')); + $response = $stub->getResponse(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $stub = $this->createApiMockObject(new Response(503, [], null)); + $response = $stub->getResponse(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $stub = $this->createApiMockObject(new Response(504, [], '{"message":"Endpoint request timed out}')); $response = $stub->getResponse(); - $this->assertEquals($responseMock->getHeaders(), $response->getHeaders()); - $this->assertEquals($responseMock->getBody(), $response->getBody()); - $this->assertEquals($responseMock->getStatusCode(), $response->getStatusCode()); - $this->assertEquals($responseMock->getProtocolVersion(), $response->getProtocolVersion()); - $this->assertEquals($responseMock->getReasonPhrase(), $response->getReasonPhrase()); + $this->assertInstanceOf(ResponseInterface::class, $response); } - public function testRequestHeaders() + public function testRequestHeaders(): void { $stub = $this->createApiMockObject( new Response(200, [], 'post-content') diff --git a/tests/CacheResponseTraitTest.php b/tests/CacheResponseTraitTest.php deleted file mode 100644 index 54977bd..0000000 --- a/tests/CacheResponseTraitTest.php +++ /dev/null @@ -1,166 +0,0 @@ -expectException(CacheException::class); - - $stub = $this->createApiMockObject( - new Response(), - ['setCacheResponse'] - ); - - $stub->newRequest('GET', '/'); - $stub->setCacheResponse($stub->request, new Response()); - } - - /** - * @return Api|MockObject - */ - public function testSetCacheInterface() - { - $stub = $this->createApiMockObject( - new Response(200, [], '{"cache": true}'), - ['setCacheInterface', 'setCacheResponse', 'getCacheResponse'] - ); - - $stub->setCacheInterface($this->getCacheInterface()); - - $this->assertTrue(true); - - return $stub; - } - - /** - * @depends testSetCacheInterface - * @param Api|MockObject $stub - * @throws \Clicksports\LexOffice\Exceptions\CacheException - * @throws \Clicksports\LexOffice\Exceptions\LexOfficeApiException - * @throws \Psr\Cache\InvalidArgumentException - */ - public function testGetCacheResponse($stub) - { - $cacheInterface = $this->getCacheInterface(); - $cacheKey = 'lex-office--v1--'; - - $stub->newRequest('GET', '/'); - - // execute response "curl" with caching in background - $response = $stub->getResponse(); - - $this->assertEquals( - $this->transformToString($response), - $this->transformToString($stub->getCacheResponse($stub->request)) - ); - - $this->assertEquals( - '{"status":200,"headers":[],"body":"{\"cache\": true}","version":"1.1","reason":"OK"}', - $cacheInterface->getItem($cacheKey)->get() - ); - - // remove cache - $cacheInterface->deleteItem($cacheKey); - } - - /** - * @depends testSetCacheInterface - * @param Api|MockObject $stub - * @throws \Clicksports\LexOffice\Exceptions\CacheException - * @throws \Psr\Cache\InvalidArgumentException - */ - public function testSetCacheResponse($stub) - { - $cacheInterface = $this->getCacheInterface(); - $cacheKey = 'lex-office--v1--'; - - $stub->newRequest('GET', '/'); - - $fakeResponse = new Response(200, [], 'fake'); - - $stub->setCacheResponse($stub->request, $fakeResponse); - - $this->assertEquals( - $cacheInterface->getItem($cacheKey)->get(), - $this->transformToString($fakeResponse) - ); - - // remove cache - $cacheInterface->deleteItem($cacheKey); - - // test on post - $stub->setCacheResponse(new Request('POST', '/'), $fakeResponse); - $this->assertNull($cacheInterface->getItem($cacheKey)->get()); - - // test on put - $stub->setCacheResponse(new Request('PUT', '/'), $fakeResponse); - $this->assertNull($cacheInterface->getItem($cacheKey)->get()); - - // test on delete - $stub->setCacheResponse(new Request('DELETE', '/'), $fakeResponse); - $this->assertNull($cacheInterface->getItem($cacheKey)->get()); - - $cacheInterface->deleteItem($cacheKey); - } - - /** - * @depends testSetCacheInterface - */ - public function testDifferentParamsCache($stub) - { - $stub->newRequest('GET', '/?a'); - $fakeResponse = new Response(200, [], 'fake'); - $stub->setCacheResponse($stub->request, $fakeResponse); - - $this->assertEquals( - $this->transformToString($stub->getCacheResponse($stub->request)), - $this->transformToString($fakeResponse) - ); - - $stub->newRequest('GET', '/?b'); - $fakeResponse = new Response(200, [], 'fake2'); - $stub->setCacheResponse($stub->request, $fakeResponse); - - $this->assertEquals( - $this->transformToString($stub->getCacheResponse($stub->request)), - $this->transformToString($fakeResponse) - ); - } - - protected function transformToString(ResponseInterface $response) - { - $obj = new stdClass(); - $obj->status = $response->getStatusCode(); - $obj->headers = $response->getHeaders(); - $obj->body = $response->getBody()->__toString(); - $obj->version = $response->getProtocolVersion(); - $obj->reason = $response->getReasonPhrase(); - - return json_encode($obj); - } -} \ No newline at end of file diff --git a/tests/Clients/ContactTest.php b/tests/Clients/ContactTest.php new file mode 100644 index 0000000..a804ce7 --- /dev/null +++ b/tests/Clients/ContactTest.php @@ -0,0 +1,88 @@ +createClientMockObject(Contact::class); + + $response = $client->getPage(0); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/contacts?page=0&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetPageWithFilters(): void + { + [$api, $client] = $this->createClientMockObject(Contact::class); + + $client->number = 12345; + $client->customer = true; + $client->vendor = false; + + $client->getPage(0); + + $this->assertEquals( + $api->apiUrl . '/v1/contacts?page=0&number=12345&customer=1&vendor=0&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testCreate(): void + { + [$api, $client] = $this->createClientMockObject(Contact::class); + + $response = $client->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/contacts', + $api->request->getUri()->__toString() + ); + } + + public function testGet(): void + { + [$api, $client] = $this->createClientMockObject(Contact::class); + + $response = $client->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/contacts/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testUpdate(): void + { + [$api, $client] = $this->createClientMockObject(Contact::class); + + $response = $client->update('resource-id', []); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('PUT', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/contacts/resource-id', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/CountryTest.php b/tests/Clients/CountryTest.php new file mode 100644 index 0000000..afd2a11 --- /dev/null +++ b/tests/Clients/CountryTest.php @@ -0,0 +1,25 @@ +createClientMockObject(Country::class); + + $response = $client->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/countries', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/CreditNoteTest.php b/tests/Clients/CreditNoteTest.php new file mode 100644 index 0000000..12b1bbb --- /dev/null +++ b/tests/Clients/CreditNoteTest.php @@ -0,0 +1,160 @@ +createClientMockObject(CreditNote::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/credit-notes/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testCreate(): void + { + [$api, $stub] = $this->createClientMockObject(CreditNote::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/credit-notes', + $api->request->getUri()->__toString() + ); + } + + public function testCreateFinalized(): void + { + [$api, $stub] = $this->createClientMockObject(CreditNote::class); + + $response = $stub->create([ + 'version' => 0 + ], true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/credit-notes?finalize=true', + $api->request->getUri()->__toString() + ); + } + + public function testGetPage(): void + { + $this->expectDeprecationV1Warning('getPage'); + + [$api, $stub] = $this->createClientMockObject(CreditNote::class); + + $response = $stub->getPage(0); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=creditnote&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + CreditNote::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=creditnote&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetVoucherListClient(): void + { + [, $stub] = $this->createClientMockObject(CreditNote::class); + + $client = $stub->getVoucherListClient(); + + $this->assertInstanceOf(VoucherList::class, $client); + } + + + public function testDocument(): void + { + [$api, $stub] = $this->createClientMockObject(CreditNote::class); + + $response = $stub->document('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/credit-notes/resource-id/document', + $api->request->getUri()->__toString() + ); + } + + public function testDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + CreditNote::class, + [ + new Response(200, ['Content-Type' => 'application/json'], '{"documentFileId": "fake-id"}'), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/fake-id', + $api->request->getUri()->__toString() + ); + } + + public function testFailedDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + CreditNote::class, + [ + new Response(500), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(500, $response->getStatusCode()); + } +} diff --git a/tests/Clients/DownPaymentInvoiceTest.php b/tests/Clients/DownPaymentInvoiceTest.php new file mode 100644 index 0000000..52db1c0 --- /dev/null +++ b/tests/Clients/DownPaymentInvoiceTest.php @@ -0,0 +1,109 @@ +createClientMockObject(DownPaymentInvoice::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/down-payment-invoices/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + DownPaymentInvoice::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=downpaymentinvoice&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetVoucherListClient(): void + { + [, $stub] = $this->createClientMockObject(DownPaymentInvoice::class); + + $client = $stub->getVoucherListClient(); + + $this->assertInstanceOf(VoucherList::class, $client); + } + + public function testDocument(): void + { + [$api, $stub] = $this->createClientMockObject(DownPaymentInvoice::class); + + $response = $stub->document('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/down-payment-invoices/resource-id/document', + $api->request->getUri()->__toString() + ); + } + + public function testDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + DownPaymentInvoice::class, + [ + new Response(200, ['Content-Type' => 'application/json'], '{"documentFileId": "fake-id"}'), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/fake-id', + $api->request->getUri()->__toString() + ); + } + + + public function testFailedDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + DownPaymentInvoice::class, + [ + new Response(500), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(500, $response->getStatusCode()); + } +} diff --git a/tests/Clients/EventTest.php b/tests/Clients/EventTest.php new file mode 100644 index 0000000..9e656d4 --- /dev/null +++ b/tests/Clients/EventTest.php @@ -0,0 +1,59 @@ +createClientMockObject(Event::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/event-subscriptions', + $api->request->getUri()->__toString() + ); + } + + public function testDelete(): void + { + [$api, $stub] = $this->createClientMockObject(Event::class); + + $response = $stub->delete('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('DELETE', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/event-subscriptions/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + [$api, $stub] = $this->createClientMockObject(Event::class); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/event-subscriptions', + $api->request->getUri()->__toString() + ); + } + +} diff --git a/tests/Clients/FileTest.php b/tests/Clients/FileTest.php new file mode 100644 index 0000000..ac3d230 --- /dev/null +++ b/tests/Clients/FileTest.php @@ -0,0 +1,102 @@ +createClientMockObject(File::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testUploadNotSupportedExtension(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(File::class); + + $stub->upload('not_allowed.gif', 'voucher'); + } + + public function testUploadNotFound(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(File::class,); + + $stub->upload('not_existing.jpg', 'voucher'); + } + + public function testUploadToBig(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(File::class); + + $this->createCacheDir(); + $file = $this->getCacheDir() . '/somefile.jpg'; + $fp = fopen($file, 'w+'); + + if ($fp === false) { + $this->fail('could not open file ' . $file); + } + + fseek($fp, VoucherConfig::MAX_FILE_SIZE + 1,SEEK_CUR); + fwrite($fp,'a'); + fclose($fp); + + $stub->upload($file, 'voucher'); + + unlink($file); + } + + public function testUploadSuccess(): void + { + [$api, $stub] = $this->createClientMockObject(File::class); + + $this->createCacheDir(); + $file = $this->getCacheDir() . '/somefile2.jpg'; + $fp = fopen($file, 'w+'); + + if ($fp === false) { + $this->fail('could not open file ' . $file); + } + + fseek($fp, 5,SEEK_CUR); + fwrite($fp,'a'); + fclose($fp); + + $response = $stub->upload($file, 'voucher'); + + unlink($file); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files', + $api->request->getUri()->__toString() + ); + + $this->assertStringContainsString( + 'multipart/form-data', + $api->request->getHeaderLine('Content-Type') + ); + } +} diff --git a/tests/Clients/InvoiceTest.php b/tests/Clients/InvoiceTest.php new file mode 100644 index 0000000..f5a4266 --- /dev/null +++ b/tests/Clients/InvoiceTest.php @@ -0,0 +1,161 @@ +createClientMockObject(Invoice::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/invoices', + $api->request->getUri()->__toString() + ); + } + + public function testCreateFinalized(): void + { + [$api, $stub] = $this->createClientMockObject(Invoice::class); + + $response = $stub->create([ + 'version' => 0 + ], true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/invoices?finalize=true', + $api->request->getUri()->__toString() + ); + } + + public function testGet(): void + { + [$api, $stub] = $this->createClientMockObject(Invoice::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/invoices/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testPage(): void + { + $this->expectDeprecationV1Warning('getPage'); + + [$api, $stub] = $this->createClientMockObject(Invoice::class); + + $response = $stub->getPage(0); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=invoice&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + Invoice::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=invoice&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetVoucherListClient(): void + { + [, $stub] = $this->createClientMockObject(Invoice::class); + + $client = $stub->getVoucherListClient(); + + $this->assertInstanceOf(VoucherList::class, $client); + } + + + public function testDocument(): void + { + [$api, $stub] = $this->createClientMockObject(Invoice::class); + + $response = $stub->document('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/invoices/resource-id/document', + $api->request->getUri()->__toString() + ); + } + + public function testDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + Invoice::class, + [ + new Response(200, ['Content-Type' => 'application/json'], '{"documentFileId": "fake-id"}'), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/fake-id', + $api->request->getUri()->__toString() + ); + } + + public function testFailedDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + Invoice::class, + [ + new Response(500), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(500, $response->getStatusCode()); + } +} diff --git a/tests/Clients/OrderConfirmationTest.php b/tests/Clients/OrderConfirmationTest.php new file mode 100644 index 0000000..65856fa --- /dev/null +++ b/tests/Clients/OrderConfirmationTest.php @@ -0,0 +1,144 @@ +createClientMockObject(OrderConfirmation::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/order-confirmations', + $api->request->getUri()->__toString() + ); + } + + public function testGet(): void + { + [$api, $stub] = $this->createClientMockObject(OrderConfirmation::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/order-confirmations/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testGetPage(): void + { + $this->expectDeprecationV1Warning('getPage'); + + [$api, $stub] = $this->createClientMockObject(OrderConfirmation::class); + + $response = $stub->getPage(0); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=orderconfirmation&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + OrderConfirmation::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=orderconfirmation&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetVoucherListClient(): void + { + [, $stub] = $this->createClientMockObject(OrderConfirmation::class); + + $client = $stub->getVoucherListClient(); + + $this->assertInstanceOf(VoucherList::class, $client); + } + + + public function testDocument(): void + { + [$api, $stub] = $this->createClientMockObject(OrderConfirmation::class); + + $response = $stub->document('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/order-confirmations/resource-id/document', + $api->request->getUri()->__toString() + ); + } + + public function testDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + OrderConfirmation::class, + [ + new Response(200, ['Content-Type' => 'application/json'], '{"documentFileId": "fake-id"}'), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/fake-id', + $api->request->getUri()->__toString() + ); + } + + public function testFailedDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + OrderConfirmation::class, + [ + new Response(500), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(500, $response->getStatusCode()); + } +} diff --git a/tests/Clients/PaymentConditionTest.php b/tests/Clients/PaymentConditionTest.php new file mode 100644 index 0000000..aca4678 --- /dev/null +++ b/tests/Clients/PaymentConditionTest.php @@ -0,0 +1,25 @@ +createClientMockObject(PaymentCondition::class); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/payment-conditions', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/PaymentTest.php b/tests/Clients/PaymentTest.php new file mode 100644 index 0000000..82dc7cf --- /dev/null +++ b/tests/Clients/PaymentTest.php @@ -0,0 +1,25 @@ +createClientMockObject(Payment::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/payment/resource-id', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/PostingCategoryTest.php b/tests/Clients/PostingCategoryTest.php new file mode 100644 index 0000000..c2c5665 --- /dev/null +++ b/tests/Clients/PostingCategoryTest.php @@ -0,0 +1,25 @@ +createClientMockObject(PostingCategory::class); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/posting-categories', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/ProfileTest.php b/tests/Clients/ProfileTest.php new file mode 100644 index 0000000..1f6f30f --- /dev/null +++ b/tests/Clients/ProfileTest.php @@ -0,0 +1,25 @@ +createClientMockObject(Profile::class); + + $response = $stub->get(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/profile', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/QuotationTest.php b/tests/Clients/QuotationTest.php new file mode 100644 index 0000000..d15b199 --- /dev/null +++ b/tests/Clients/QuotationTest.php @@ -0,0 +1,143 @@ +createClientMockObject(Quotation::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/quotations', + $api->request->getUri()->__toString() + ); + } + + public function testCreateFinalized(): void + { + [$api, $stub] = $this->createClientMockObject(Quotation::class); + + $response = $stub->create([ + 'version' => 0 + ], true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/quotations?finalize=true', + $api->request->getUri()->__toString() + ); + } + + public function testGet(): void + { + [$api, $stub] = $this->createClientMockObject(Quotation::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/quotations/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + Quotation::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=quotation&voucherStatus=draft%2Copen%2Cpaid%2Cpaidoff%2Cvoided%2Caccepted%2Crejected&size=100', + $api->request->getUri()->__toString() + ); + } + + public function testGetVoucherListClient(): void + { + [, $stub] = $this->createClientMockObject(Quotation::class); + + $client = $stub->getVoucherListClient(); + + $this->assertInstanceOf(VoucherList::class, $client); + } + + public function testDocument(): void + { + [$api, $stub] = $this->createClientMockObject(Quotation::class); + + $response = $stub->document('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/quotations/resource-id/document', + $api->request->getUri()->__toString() + ); + } + + public function testDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + Quotation::class, + [ + new Response(200, ['Content-Type' => 'application/json'], '{"documentFileId": "fake-id"}'), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/files/fake-id', + $api->request->getUri()->__toString() + ); + } + + public function testFailedDocumentContent(): void + { + [$api, $stub] = $this->createClientMultiMockObject( + Quotation::class, + [ + new Response(500), + new Response() + ] + ); + + $response = $stub->document('resource-id', true); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(500, $response->getStatusCode()); + } +} diff --git a/tests/Clients/RecurringTemplateTest.php b/tests/Clients/RecurringTemplateTest.php new file mode 100644 index 0000000..126ee64 --- /dev/null +++ b/tests/Clients/RecurringTemplateTest.php @@ -0,0 +1,39 @@ +createClientMockObject(RecurringTemplate::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/recurring-templates/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testGetPage(): void + { + [$api, $stub] = $this->createClientMockObject(RecurringTemplate::class); + + $stub->getPage(0); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/recurring-templates?page=0&size=100&sort=updatedDate%2CDESC', + $api->request->getUri()->__toString() + ); + } + +} diff --git a/tests/Clients/VoucherListTest.php b/tests/Clients/VoucherListTest.php new file mode 100644 index 0000000..d3b83ad --- /dev/null +++ b/tests/Clients/VoucherListTest.php @@ -0,0 +1,55 @@ +createClientMockObject(VoucherList::class); + + $stub->types = ['invoice']; + $stub->statuses = ['open']; + $stub->archived = true; + + $response = $stub->getPage(0); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&voucherType=invoice&voucherStatus=open&archived=1&size=100&sort=voucherNumber%2CDESC', + $api->request->getUri()->__toString() + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + VoucherList::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $stub->types = ['invoice']; + $stub->statuses = ['open']; + $stub->archived = true; + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&voucherType=invoice&voucherStatus=open&archived=1&size=100&sort=voucherNumber%2CDESC', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Clients/VoucherTest.php b/tests/Clients/VoucherTest.php new file mode 100644 index 0000000..858200c --- /dev/null +++ b/tests/Clients/VoucherTest.php @@ -0,0 +1,156 @@ +createClientMockObject(Voucher::class); + + $response = $stub->create([ + 'version' => 0 + ]); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/vouchers', + $api->request->getUri()->__toString() + ); + } + + public function testGet(): void + { + [$api, $stub] = $this->createClientMockObject(Voucher::class); + + $response = $stub->get('resource-id'); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/vouchers/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testUpdate(): void + { + [$api, $stub] = $this->createClientMockObject(Voucher::class); + + $response = $stub->update('resource-id', []); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('PUT', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/vouchers/resource-id', + $api->request->getUri()->__toString() + ); + } + + public function testUploadNotSupportedExtension(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(Voucher::class); + + $stub->upload('resource-id', 'not_allowed.gif'); + } + + public function testUploadNotFound(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(Voucher::class,); + + $stub->upload('resource-id', 'not_existing.jpg'); + } + + public function testUploadToBig(): void + { + $this->expectException(LexOfficeApiException::class); + + [, $stub] = $this->createClientMockObject(Voucher::class); + + $this->createCacheDir(); + $file = $this->getCacheDir() . '/somefile.jpg'; + $fp = fopen($file, 'w+'); + + if ($fp === false) { + $this->fail('could not open file ' . $file); + } + + fseek($fp, VoucherConfig::MAX_FILE_SIZE + 1,SEEK_CUR); + fwrite($fp,'a'); + fclose($fp); + + $stub->upload('resource-id', $file); + + unlink($file); + } + + public function testUploadSuccess(): void + { + [$api, $stub] = $this->createClientMockObject(Voucher::class); + + $this->createCacheDir(); + $file = $this->getCacheDir() . '/somefile2.jpg'; + $fp = fopen($file, 'w+'); + + if ($fp === false) { + $this->fail('could not open file ' . $file); + } + + fseek($fp, 5,SEEK_CUR); + fwrite($fp,'a'); + fclose($fp); + + $response = $stub->upload('resource-id', $file); + + unlink($file); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('POST', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/vouchers/resource-id', + $api->request->getUri()->__toString() + ); + + $this->assertStringContainsString( + 'multipart/form-data', + $api->request->getHeaderLine('Content-Type') + ); + } + + public function testGetAll(): void + { + $this->expectDeprecationV1Warning('getAll'); + + [$api, $stub] = $this->createClientMultiMockObject( + Voucher::class, + [new Response(200, [], '{"content": [], "totalPages": 1}')] + ); + + $response = $stub->getAll(); + + $this->assertInstanceOf(ResponseInterface::class, $response); + + $this->assertEquals('GET', $api->request->getMethod()); + $this->assertEquals( + $api->apiUrl . '/v1/voucherlist?page=0&sort=voucherNumber%2CDESC&voucherType=salesinvoice%2Csalescreditnote%2Cpurchaseinvoice%2Cpurchasecreditnote&voucherStatus=open%2Cpaid%2Cpaidoff%2Cvoided%2Ctransferred%2Csepadebit&size=100', + $api->request->getUri()->__toString() + ); + } +} diff --git a/tests/Contact/ClientTest.php b/tests/Contact/ClientTest.php deleted file mode 100644 index 63d608c..0000000 --- a/tests/Contact/ClientTest.php +++ /dev/null @@ -1,65 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['generateUrl'] - ); - - $this->assertEquals( - 'contacts?page=0&size=100&direction=ASC&property=name', - $stub->generateUrl(0) - ); - } - - public function testCreate() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['update'] - ); - - $response = $stub->update('resource-id', []); - - $this->assertEquals('body', $response->getBody()->__toString()); - } -} diff --git a/tests/Country/ClientTest.php b/tests/Country/ClientTest.php deleted file mode 100644 index 3c064a9..0000000 --- a/tests/Country/ClientTest.php +++ /dev/null @@ -1,64 +0,0 @@ -expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([]); - } - - public function testGet() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $stub->get('resource-id'); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } -} diff --git a/tests/CreditNote/ClientTest.php b/tests/CreditNote/ClientTest.php deleted file mode 100644 index 821aa4f..0000000 --- a/tests/CreditNote/ClientTest.php +++ /dev/null @@ -1,70 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testDocument() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"documentFileId": "fake-id"}'), - ['document'] - ); - - $response = $stub->document('resource-id'); - - $this->assertEquals( - '{"documentFileId": "fake-id"}', - $response->getBody()->__toString() - ); - - $stub = $this->createClientMultiMockObject( - Client::class, - [ - new Response(200, [], '{"documentFileId": "fake-id"}'), - new Response(200, [], '{}') - ], - ['document'] - ); - - $response = $stub->document('resource-id', true); - - $this->assertEquals( - '{}', - $response->getBody()->__toString() - ); - } -} diff --git a/tests/DeprecationException.php b/tests/DeprecationException.php new file mode 100644 index 0000000..051f73d --- /dev/null +++ b/tests/DeprecationException.php @@ -0,0 +1,8 @@ +expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([ - 'version' => 0 - ]); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['update'] - ); - - $stub->update('resource-id', []); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testDocument() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"documentFileId": "fake-id"}'), - ['document'] - ); - - $response = $stub->document('resource-id'); - - $this->assertEquals( - '{"documentFileId": "fake-id"}', - $response->getBody()->__toString() - ); - - $stub = $this->createClientMultiMockObject( - Client::class, - [ - new Response(200, [], '{"documentFileId": "fake-id"}'), - new Response(200, [], '{}') - ], - ['document'] - ); - - $response = $stub->document('resource-id', true); - - $this->assertEquals( - '{}', - $response->getBody()->__toString() - ); - } -} diff --git a/tests/Event/ClientTest.php b/tests/Event/ClientTest.php deleted file mode 100644 index 53b652b..0000000 --- a/tests/Event/ClientTest.php +++ /dev/null @@ -1,80 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $stub->get('resource-id'); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } - - public function testDelete() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['delete'] - ); - - $response = $stub->delete('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - -} diff --git a/tests/File/ClientTest.php b/tests/File/ClientTest.php deleted file mode 100644 index 4a25f9f..0000000 --- a/tests/File/ClientTest.php +++ /dev/null @@ -1,107 +0,0 @@ -expectException(LexOfficeApiException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['upload'] - ); - - $stub->upload('not_allowed.gif', 'voucher'); - } - - public function testUploadNotFound() - { - $this->expectException(LexOfficeApiException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['upload'] - ); - - $stub->upload('not_existing.jpg', 'voucher'); - } - - public function testUploadToBig() - { - $this->expectException(LexOfficeApiException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['upload'] - ); - - $file = __DIR__ . '/somefile.jpg'; - $fp = fopen($file, 'w+'); // - fseek($fp, Client::MAX_FILE_SIZE + 1,SEEK_CUR); - fwrite($fp,'a'); - fclose($fp); - - $stub->upload($file, 'voucher'); - - unlink($file); - } - - public function testUpload() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['upload'] - ); - - $file = __DIR__ . '/somefile2.jpg'; - $fp = fopen($file, 'w+'); // - fseek($fp, 5,SEEK_CUR); - fwrite($fp,'a'); - fclose($fp); - - $response = $stub->upload($file, 'voucher'); - - unlink($file); - - $this->assertInstanceOf(ResponseInterface::class, $response); - } - - public function testCreate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['create'] - ); - - $stub->create([]); - } - - public function testGetAll() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['getAll'] - ); - - $stub->getAll(); - } -} diff --git a/tests/Invoice/ClientTest.php b/tests/Invoice/ClientTest.php deleted file mode 100644 index d87a136..0000000 --- a/tests/Invoice/ClientTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } - - public function testDocument() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"documentFileId": "fake-id"}'), - ['document'] - ); - - $response = $stub->document('resource-id'); - - $this->assertEquals( - '{"documentFileId": "fake-id"}', - $response->getBody()->__toString() - ); - - $stub = $this->createClientMultiMockObject( - Client::class, - [ - new Response(200, [], '{"documentFileId": "fake-id"}'), - new Response(200, [], '{}') - ], - ['document'] - ); - - $response = $stub->document('resource-id', true); - - $this->assertEquals( - '{}', - $response->getBody()->__toString() - ); - } -} diff --git a/tests/OrderConfirmation/ClientTest.php b/tests/OrderConfirmation/ClientTest.php deleted file mode 100644 index d91d22a..0000000 --- a/tests/OrderConfirmation/ClientTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } - - public function testDocument() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"documentFileId": "fake-id"}'), - ['document'] - ); - - $response = $stub->document('resource-id'); - - $this->assertEquals( - '{"documentFileId": "fake-id"}', - $response->getBody()->__toString() - ); - - $stub = $this->createClientMultiMockObject( - Client::class, - [ - new Response(200, [], '{"documentFileId": "fake-id"}'), - new Response(200, [], '{}') - ], - ['document'] - ); - - $response = $stub->document('resource-id', true); - - $this->assertEquals( - '{}', - $response->getBody()->__toString() - ); - } -} diff --git a/tests/PaginationClientTest.php b/tests/PaginationClientTest.php index 6a81fd9..79ca03d 100644 --- a/tests/PaginationClientTest.php +++ b/tests/PaginationClientTest.php @@ -1,44 +1,79 @@ -createPaginationClientMockObject( - [new Response()], - ['generateUrl'] - ); + $api = $this->createApiMultiMockObject($responses); - $this->assertEquals( - 'resource?page=0&size=100', - $stub->generateUrl(0) - ); + $stub = $this + ->getMockBuilder(PaginationClient::class) + ->onlyMethods([]) + ->setConstructorArgs([$api]) + ->getMock(); + + $this->setProtectedProperty($stub, 'resource', 'resource'); + + return [$api, $stub]; + } + + /** + * Sets a protected property on a given object via reflection + * + * @param object $object - instance in which protected value is being modified + * @param string $property - property on instance being modified + * @param mixed $value - new value of the property being modified + * + * @return void + * + * @throws ReflectionException + * @link https://stackoverflow.com/a/37667018/7387397 + */ + public function setProtectedProperty(object $object, string $property, $value) + { + $reflection = new ReflectionClass($object); + $reflection_property = $reflection->getProperty($property); + $reflection_property->setAccessible(true); + $reflection_property->setValue($object, $value); } - public function testGetAll() + public function testGetAllSingle(): void { - $stub = $this->createPaginationClientMockObject( - [new Response(200, [], '{"content": [], "totalPages": 1}')], - ['getAll', 'getPage'] + $this->expectDeprecationV1Warning('getAll'); + + [, $stub] = $this->createPaginationClientMockObject( + [new Response(200, [], '{"content": [], "totalPages": 1}')] ); $this->assertEquals( '{"content": [], "totalPages": 1}', $stub->getAll()->getBody()->__toString() ); + } + public function testGetAllMultiple(): void + { + $this->expectDeprecationV1Warning('getAll'); - $stub = $this->createPaginationClientMockObject( + [, $stub] = $this->createPaginationClientMockObject( [ - new Response(200, [], '{"content": [{"name": "a"}], "totalPages": 2}'), - new Response(200, [], '{"content": [{"name": "b"}], "totalPages": 2}') - ], - ['getAll', 'getPage'] + new Response(200, ['Content-Type' => 'application/json'], '{"content": [{"name": "a"}], "totalPages": 2}'), + new Response(200, ['Content-Type' => 'application/json'], '{"content": [{"name": "b"}], "totalPages": 2}') + ] ); $this->assertEquals( @@ -47,16 +82,17 @@ public function testGetAll() ); } - public function testGetPage() + public function testGetPage(): void { - $stub = $this->createPaginationClientMockObject( - [new Response(200, [], '{"content": [], "totalPages": 1}')], - ['getPage'] + [$api, $stub] = $this->createPaginationClientMockObject( + [new Response(200, [], '{"content": [], "totalPages": 1}')] ); + $stub->getPage(0); + $this->assertEquals( - '{"content": [], "totalPages": 1}', - $stub->getPage(0)->getBody()->__toString() + $api->apiUrl . '/v1/resource?page=0&size=100', + $api->request->getUri()->__toString() ); } } diff --git a/tests/Payment/ClientTest.php b/tests/Payment/ClientTest.php deleted file mode 100644 index cd95e62..0000000 --- a/tests/Payment/ClientTest.php +++ /dev/null @@ -1,64 +0,0 @@ -expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([]); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['getAll'] - ); - - $stub->getAll(); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } -} diff --git a/tests/PaymentCondition/ClientTest.php b/tests/PaymentCondition/ClientTest.php deleted file mode 100644 index 7ad522d..0000000 --- a/tests/PaymentCondition/ClientTest.php +++ /dev/null @@ -1,63 +0,0 @@ -expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([]); - } - - public function testGet() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $stub->get('resource-id'); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } -} diff --git a/tests/PostingCategory/ClientTest.php b/tests/PostingCategory/ClientTest.php deleted file mode 100644 index c27b2ab..0000000 --- a/tests/PostingCategory/ClientTest.php +++ /dev/null @@ -1,66 +0,0 @@ -expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([ - 'version' => 0 - ]); - } - - public function testGet() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $stub->get('resource-id'); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } -} diff --git a/tests/Profile/ClientTest.php b/tests/Profile/ClientTest.php deleted file mode 100644 index b2a905e..0000000 --- a/tests/Profile/ClientTest.php +++ /dev/null @@ -1,65 +0,0 @@ -expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['getAll'] - ); - - $stub->getAll(); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['get'] - ); - - $this->assertEquals( - '{}', - $stub->get()->getBody()->__toString() - ); - } - - public function testCreate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['create'] - ); - - $stub->create([]); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('id', []); - } -} diff --git a/tests/Quotation/ClientTest.php b/tests/Quotation/ClientTest.php deleted file mode 100644 index a0776be..0000000 --- a/tests/Quotation/ClientTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } - - public function testDocument() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"documentFileId": "fake-id"}'), - ['document'] - ); - - $response = $stub->document('resource-id'); - - $this->assertEquals( - '{"documentFileId": "fake-id"}', - $response->getBody()->__toString() - ); - - $stub = $this->createClientMultiMockObject( - Client::class, - [ - new Response(200, [], '{"documentFileId": "fake-id"}'), - new Response(200, [], '{}') - ], - ['document'] - ); - - $response = $stub->document('resource-id', true); - - $this->assertEquals( - '{}', - $response->getBody()->__toString() - ); - } -} diff --git a/tests/RecurringTemplate/ClientTest.php b/tests/RecurringTemplate/ClientTest.php deleted file mode 100644 index ca3717f..0000000 --- a/tests/RecurringTemplate/ClientTest.php +++ /dev/null @@ -1,25 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['generateUrl'] - ); - - $this->assertEquals( - 'recurring-templates?page=0&size=100', - $stub->generateUrl(0) - ); - } - -} diff --git a/tests/TestClient.php b/tests/TestClient.php index ebd86bd..d0c63c6 100644 --- a/tests/TestClient.php +++ b/tests/TestClient.php @@ -1,130 +1,105 @@ -createApiMultiMockObject([$response], $methodExcept); + return $this->createApiMultiMockObject([$response]); } /** - * @param array $responses - * @param array $methodExcept - * @return MockObject|Api + * @param Response[] $responses + * @return Api&MockObject */ - public function createApiMultiMockObject(array $responses, $methodExcept = []) + public function createApiMultiMockObject(array $responses) { $responseMock = new MockHandler($responses); return $this ->getMockBuilder(Api::class) + ->onlyMethods([]) ->setConstructorArgs([ '', new Client([ 'handler' => HandlerStack::create($responseMock) ]) ]) - ->setMethodsExcept([ - ...$methodExcept, - 'setRequest', - 'newRequest', - 'getResponse' - ]) ->getMock(); } /** - * @param string $className - * @param Response $response - * @param array $methodExcept - * @return MockObject|ClientInterface + * @template T of ClientInterface + * @param class-string $className + * @return array{0: Api&MockObject, 1: T&MockObject} */ - public function createClientMockObject(string $className, Response $response, array $methodExcept = []) + public function createClientMockObject(string $className): array { - return $this->createClientMultiMockObject($className, [$response], $methodExcept); + return $this->createClientMultiMockObject($className, [new Response()]); } /** - * @param string $className + * @template T of ClientInterface + * @param class-string $className * @param Response[] $responses - * @param array $methodExcept - * @return MockObject + * @return array{0: Api&MockObject, 1: T&MockObject} */ - public function createClientMultiMockObject(string $className, array $responses, array $methodExcept = []) + public function createClientMultiMockObject(string $className, array $responses): array { $api = $this->createApiMultiMockObject($responses); - return $this + $client = $this ->getMockBuilder($className) + ->onlyMethods([]) ->setConstructorArgs([$api]) - ->setMethodsExcept([ - ...$methodExcept, - 'getAsJson', - 'createStream' - ]) ->getMock(); + + return [$api, $client]; } - /** - * @param array $responses - * @param array $methodExcept - * @return MockObject|PaginationClient - * @throws ReflectionException - */ - public function createPaginationClientMockObject(array $responses, array $methodExcept = []) + public function createCacheDir(): void { - $api = $this->createApiMultiMockObject($responses); + $dir = $this->getCacheDir(); - $stub = $this - ->getMockBuilder(PaginationClient::class) - ->setConstructorArgs([$api]) - ->setMethodsExcept([ - ...$methodExcept, - 'getAsJson' - ]) - ->getMock(); - - $this->setProtectedProperty($stub, 'resource', 'resource'); + if (!is_dir($dir)) { + mkdir($dir); + } + } - return $stub; + public function getCacheDir(): string + { + return __DIR__ . '/cache'; } - /** - * Sets a protected property on a given object via reflection - * - * @param $object - instance in which protected value is being modified - * @param $property - property on instance being modified - * @param $value - new value of the property being modified - * - * @return void - * - * @throws ReflectionException - * @link https://stackoverflow.com/a/37667018/7387397 - */ - public function setProtectedProperty($object, $property, $value) + public function expectDeprecationV1Warning(string $method): void { - $reflection = new ReflectionClass($object); - $reflection_property = $reflection->getProperty($property); - $reflection_property->setAccessible(true); - $reflection_property->setValue($object, $value); + set_error_handler(static function (int $errno, string $errstr): void { + throw new DeprecationException($errstr, $errno); + }, E_USER_DEPRECATED); + + $this->expectException(DeprecationException::class); + + // we can not check for full class names, because we are mocking objects + $this->expectExceptionMessage('::' . $method . ' should not be called anymore, in future versions this method WILL not exist'); } } \ No newline at end of file diff --git a/tests/Utils/WarningContainer.php b/tests/Utils/WarningContainer.php new file mode 100644 index 0000000..2eff1d7 --- /dev/null +++ b/tests/Utils/WarningContainer.php @@ -0,0 +1,8 @@ +createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $response = $stub->create([ - 'version' => 0 - ]); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGet() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $response = $stub->get('resource-id'); - - $this->assertEquals('body', $response->getBody()->__toString()); - } - - public function testGetAll() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{"content": [], "totalPages": 1}'), - ['getAll'] - ); - - $response = $stub->getAll(); - - $this->assertEquals('{"content": [], "totalPages": 1}', $response->getBody()->__toString()); - } - - public function testUpdate() - { - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $response = $stub->update('resource-id', []); - - $this->assertEquals('{}', $response->getBody()->__toString()); - } -} diff --git a/tests/Voucherlist/ClientTest.php b/tests/Voucherlist/ClientTest.php deleted file mode 100644 index a3bca0f..0000000 --- a/tests/Voucherlist/ClientTest.php +++ /dev/null @@ -1,68 +0,0 @@ -createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['generateUrl'] - ); - - $stub->types = ['invoice']; - $stub->statuses = ['open']; - - $this->assertEquals( - 'voucherlist?page=0&size=100&sort=voucherNumber,DESC&voucherType=invoice&voucherStatus=open', - $stub->generateUrl(0) - ); - } - - public function testCreate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['create'] - ); - - $stub->create([]); - } - - public function testGet() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], 'body'), - ['get'] - ); - - $stub->get('resource-id'); - } - - public function testUpdate() - { - $this->expectException(BadMethodCallException::class); - - $stub = $this->createClientMockObject( - Client::class, - new Response(200, [], '{}'), - ['update'] - ); - - $stub->update('resource-id', []); - } -}