├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── exemplu.php ├── gen-docs.bat ├── gen-docs.sh ├── run-tests-example.bat ├── run-tests-example.sh ├── src ├── ANAFAPIClient.php ├── ANAFEntity │ ├── Address.php │ ├── Entity.php │ ├── GeneralInfo.php │ ├── InactiveInfo.php │ ├── RTVAInfo.php │ ├── SplitTVAInfo.php │ └── TVAInfo.php └── Responses │ ├── ANAFAnswer.php │ ├── ANAFAnswerListResponse.php │ ├── ANAFException.php │ ├── ANAFResponse.php │ ├── ANAFVerifyResponse.php │ ├── EntityResponse.php │ ├── InternalPagedAnswersResponse.php │ ├── PagedAnswerListResponse.php │ ├── TVAResponse.php │ └── UBLUploadResponse.php └── tests ├── ParseTests ├── AnswerListTest.php ├── EntityResponseTest.php ├── UploadResponseTest.php └── VerifyResponseTest.php └── RequestTests ├── AccessTokenTest.php ├── AnswerListAnswerTest.php ├── RequestTestBase.php └── UploadUBLTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /.idea/ 3 | test_load.php 4 | phpDocumentor.phar 5 | .phpdoc/ 6 | ANAFAccessToken.json 7 | run-tests.bat -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v2.0.4 2 | - Fixed a bug that could lead to a runtime error if unexpected statusInactivi response was recived when loading a companies info 3 | - Moved to new (v9) API endpoint for loading company info 4 | 5 | 6 | # v2.0.3 7 | - Added error code MESSAGE_LIST_TOO_LONG = 13 to ANAFException 8 | - ANAFAnswerListResponse now checks if recived error states that message count is to high for a non paged answer list and uses the appropriate error code if so 9 | - ANAFAAPIClient::ListAnswers now has 2 new optional parameters that allow it to fall back to ListAnswersWithPagination if the after receiving an MESSAGE_LIST_TOO_LONG exception 10 | 11 | # v2.0.2-beta 12 | - ANAFAPIClient new has a new method TokenWillExpireSoon that returns true of the token will expire in 24h or less 13 | - TokenWillExpireSoon is used to determine when to auto refresh the token 14 | - ListAnswers now support filtering 15 | - Added private ValidateFilter to check for valid filter chars 16 | - Invalid filters will create an INVALID_INPUT exception 17 | - Added ListAnswersWithPagination will return an answer list containing all pages. 18 | - PHPUnit version changed from 11 to ^11 19 | - Minimum stability changed from alpha to stable 20 | - Updated some dependencies 21 | 22 | 23 | # v2.0.1-beta 24 | ANAFAPIClient->AccessToken is now protected (was private) 25 | # v2.0.0-beta 26 | In order to update/cleanup the code I've jumped to a new major version. Please read the breaking changes section below before you update. 27 | # BREAKING CHANGE 28 | Mostly internal changes: 29 | - ANAFAPIResponse renamed to ANAFResponse 30 | - Added ANAFResponse::LastError to store the last error message, use LastError->getMessage() and LastError->getCode() to get more info 31 | - ANAFResponse::success removed, use ANAFResponse::IsSuccess() or ANAFResponse::HasError() instead 32 | - ANAFResponse::message removed, use ANAFResponse::LastError to get info 33 | - ANAFResponse::LastParseError removed 34 | - ANAFResponse::Parse return type changed from bool to void 35 | - ANAFAnswerListResponse now extends ANAFResponse and success and eroare properties removed 36 | - Removed CreateFromParsed from all implementers of ANAFResponse 37 | - ANAFAPIClient::VerifyXML no longer returns bool, returns ANAFVerifyResponse instead 38 | - UBLUploadResponse no longer stores index_incarcare in message, has its own field UBLLoadResponse::IndexIncarcare 39 | - ANAFAPIClient::DoEntityFetch now only takes 2 arguments (third was unused) 40 | 41 | Non breaking changes: 42 | - ANAFAPIClient no longer extends GuzzleHttp\Client 43 | - ANAFAPIClient no longer uses fully formed URLs for API calls 44 | - Added ANAFAPIClient::LockToken to facilitate "locking" the token file by the test suite (Token will not be refreshed or saved during testing) 45 | - ANAFAPIClient::RemoveSchemaLocationAttribute is public static now 46 | - ANAFAPIClient::SendANAFRequest and ANAFAPIClient::UBL2PDF now both have a new optional parameter to override the default timeout. (5s is sometime not enough) 47 | - ANAFAPIClient::VerifyXML and ANAFAPIClient::UBL2PDF now have a new optional boolean parameter called $authenticated. If set to true the OAuth2 API endpoint will be used instead of the public endpoint. By default this is true! 48 | - ANAFResponse defines two new abstract function Create and CreateError 49 | - Added ANAFException class, mostly to hold error code constants. 50 | - Removed (unused) dependency/ using statement for DOMDocument 51 | - Added new dependency ext-simplexml 52 | - Added PHUnit as a dev dependency 53 | - Wrote tests for all response parsing 54 | - Wrote tests for some API calls 55 | - Fixed an issue where UBl2PDF failed if the client was in testing mode (the API has no backend for testing) 56 | - Added run-tests-example.bat 57 | 58 | 59 | # v1.1.1-alpha 60 | - RemoveSchemaLocationAttribute no longer uses DOMDocument to strip xsi:schemaLocation attribute. Uses regex instead! (DOMDocument caused side effects with special characters that could cause UBL2PDF to fail). 61 | - Removed, now unnecessary, dependencies (ext-dom and ex-libxml) 62 | 63 | # v1.1.0-alpha 64 | - LoadAccessToken() no longer causes unhandled exception when file does not exist. 65 | - Location of the token file is now configurable. (Check $TokenFilePath and ANAFAPIClient::__construct()) 66 | - Moved token load/refresh logic from HasAccessToken() to LoadAccessToken() and RefreshAccessToken(). 67 | - Added optional parameter to LoadAccessToken() to enable/disable automatic token refresh. 68 | - HasAccessToken() now calls LoadAccessToken() with automatic refresh enabled. (logic moved) 69 | - The error callback method now has a second, optional, parameter: ?Throwable $ex = null. 70 | - Removed some unnecessary error_log calls. 71 | - General code cleanup. 72 | 73 | BREAKING CHANGES: 74 | ================= 75 | - HasToken() renamed to HasAccessToken(). (for consistency) 76 | - GetAccessToken() no longer has a parameter and serves as a getter for $AccessToken instead of loading the token from ANAF. Functionality as it was moved to a new method (ProcessOAuthCallback). 77 | - RefreshToken() renamed to RefreshAccessToken(). (for consistency) 78 | - SaveAccessToken() is now private (called automatically by ProcessOAuthCallback and RefreshAccessToken). 79 | - UploadEFactura() added a new (required) parameter: $sellerCIF (previously the CIF was hardcoded) 80 | 81 | 82 | # v1.0.4-alpha 83 | Added timeout to all outgoing requests (5s by default) 84 | 85 | # v1.0.3-alpha 86 | Add extern and autofactura params for UploadEFactura 87 | 88 | # v1.0.2-alpha 89 | Creating composer package 90 | Moved classes to new namespace (EdituraEDU\ANAF) 91 | Clarified extension dependencies (ext-dom and ext-libxml) 92 | 93 | # v1.0.1 94 | New dependency: php-xml(extension) 95 | Cleaned up some error_log calls. 96 | Added ListAnswers API call. 97 | Added DownloadAnswer API call. 98 | Added UBL2PDF API call. 99 | Added VerifyXML API call. IMPORANT: At the time of writing the API responses are unpredicatable theirfor this method should not be used! 100 | Fixed some typos. 101 | 102 | # v1.0.0 103 | Initial release 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tecsi Aron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ANAF-API-Client-PHP 2 | Citeste CHANGELOG.md inainte de a face update de la v1.1.1-alpha la v2.0.0-beta 3 | API ANAF pentru interogare CIF si upload RO-eFactura. 4 | Foloseste PHP 8+ 5 | # Versiunea minima de PHP se va schimba din 8.0 in 8.1 in viitorul apropriat! 6 | Pentru a instala: 7 | ``` 8 | composer require tecsiaron/anaf-api-client-php 9 | ``` 10 | Exemplu: 11 | ```phg 12 | getMessage(); 20 | } 21 | }; 22 | $anaf = new ANAFAPIClient(ANAF_OAUTH,false, $Logger); 23 | var_dump($anaf->GetEntity("RO12345678")); 24 | ``` 25 | Formatul pentru datele de oauth: 26 | 27 | ``` 28 | const ANAF_OAUTH=[ 29 | 'clientId' => 'client_id_din_contul_de_dezvoltator', 30 | 'clientSecret' => 'client_secret', 31 | 'redirectUri' => 'redirect_url', 32 | 'urlAuthorize' => 'https://logincert.anaf.ro/anaf-oauth2/v1/authorize', 33 | 'urlAccessToken' => 'https://logincert.anaf.ro/anaf-oauth2/v1/token', 34 | 'urlResourceOwnerDetails' => 'https://logincert.anaf.ro/anaf-oauth2/v1/resource' 35 | ]; 36 | ``` 37 | Documentatie: https://tecsiaron.github.io/ANAF-API-Client-PHP/ 38 | # Functii: 39 | - Accesarea registrului de inregistrari in scopuri de TVA 40 | - Autentificare OAuth 41 | - Incarcarea unei facturi UBL in sistemul RO e-Factura 42 | - Validare UBL prin API ANAF (API instabil) 43 | - Conversie UBL in PDF prin API ANAF 44 | - Listare raspunsuri din SPV 45 | - Descarcare raspuns din SPV 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tecsiaron/anaf-api-client-php", 3 | "description": "PHP API Client for ANAF API and Ro eFactura", 4 | "keywords": [ 5 | "anaf", 6 | "api", 7 | "cif", 8 | "api client", 9 | "roefactura", 10 | "efactura" 11 | ], 12 | "type": "library", 13 | "require": { 14 | "guzzlehttp/guzzle": "^7.0", 15 | "league/oauth2-client": "^2.7", 16 | "php": ">=8.0", 17 | "ext-simplexml": "*" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "EdituraEDU\\ANAF\\": "src/" 22 | } 23 | }, 24 | "autoload-dev": { 25 | "psr-4": { 26 | "EdituraEDU\\ANAF\\Tests\\": "tests/" 27 | } 28 | }, 29 | "license": "MIT", 30 | "authors": [ 31 | { 32 | "name": "Tecsi Aron", 33 | "email": "aron@edituraedu.ro" 34 | } 35 | ], 36 | "minimum-stability": "stable", 37 | "require-dev": { 38 | "phpunit/phpunit": "^11" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "46f2d57e51a7e692afdae01cc0cc9677", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/guzzle", 11 | "version": "7.9.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/guzzle.git", 15 | "reference": "d281ed313b989f213357e3be1a179f02196ac99b" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", 20 | "reference": "d281ed313b989f213357e3be1a179f02196ac99b", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-json": "*", 25 | "guzzlehttp/promises": "^1.5.3 || ^2.0.3", 26 | "guzzlehttp/psr7": "^2.7.0", 27 | "php": "^7.2.5 || ^8.0", 28 | "psr/http-client": "^1.0", 29 | "symfony/deprecation-contracts": "^2.2 || ^3.0" 30 | }, 31 | "provide": { 32 | "psr/http-client-implementation": "1.0" 33 | }, 34 | "require-dev": { 35 | "bamarni/composer-bin-plugin": "^1.8.2", 36 | "ext-curl": "*", 37 | "guzzle/client-integration-tests": "3.0.2", 38 | "php-http/message-factory": "^1.1", 39 | "phpunit/phpunit": "^8.5.39 || ^9.6.20", 40 | "psr/log": "^1.1 || ^2.0 || ^3.0" 41 | }, 42 | "suggest": { 43 | "ext-curl": "Required for CURL handler support", 44 | "ext-intl": "Required for Internationalized Domain Name (IDN) support", 45 | "psr/log": "Required for using the Log middleware" 46 | }, 47 | "type": "library", 48 | "extra": { 49 | "bamarni-bin": { 50 | "bin-links": true, 51 | "forward-command": false 52 | } 53 | }, 54 | "autoload": { 55 | "files": [ 56 | "src/functions_include.php" 57 | ], 58 | "psr-4": { 59 | "GuzzleHttp\\": "src/" 60 | } 61 | }, 62 | "notification-url": "https://packagist.org/downloads/", 63 | "license": [ 64 | "MIT" 65 | ], 66 | "authors": [ 67 | { 68 | "name": "Graham Campbell", 69 | "email": "hello@gjcampbell.co.uk", 70 | "homepage": "https://github.com/GrahamCampbell" 71 | }, 72 | { 73 | "name": "Michael Dowling", 74 | "email": "mtdowling@gmail.com", 75 | "homepage": "https://github.com/mtdowling" 76 | }, 77 | { 78 | "name": "Jeremy Lindblom", 79 | "email": "jeremeamia@gmail.com", 80 | "homepage": "https://github.com/jeremeamia" 81 | }, 82 | { 83 | "name": "George Mponos", 84 | "email": "gmponos@gmail.com", 85 | "homepage": "https://github.com/gmponos" 86 | }, 87 | { 88 | "name": "Tobias Nyholm", 89 | "email": "tobias.nyholm@gmail.com", 90 | "homepage": "https://github.com/Nyholm" 91 | }, 92 | { 93 | "name": "Márk Sági-Kazár", 94 | "email": "mark.sagikazar@gmail.com", 95 | "homepage": "https://github.com/sagikazarmark" 96 | }, 97 | { 98 | "name": "Tobias Schultze", 99 | "email": "webmaster@tubo-world.de", 100 | "homepage": "https://github.com/Tobion" 101 | } 102 | ], 103 | "description": "Guzzle is a PHP HTTP client library", 104 | "keywords": [ 105 | "client", 106 | "curl", 107 | "framework", 108 | "http", 109 | "http client", 110 | "psr-18", 111 | "psr-7", 112 | "rest", 113 | "web service" 114 | ], 115 | "support": { 116 | "issues": "https://github.com/guzzle/guzzle/issues", 117 | "source": "https://github.com/guzzle/guzzle/tree/7.9.2" 118 | }, 119 | "funding": [ 120 | { 121 | "url": "https://github.com/GrahamCampbell", 122 | "type": "github" 123 | }, 124 | { 125 | "url": "https://github.com/Nyholm", 126 | "type": "github" 127 | }, 128 | { 129 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", 130 | "type": "tidelift" 131 | } 132 | ], 133 | "time": "2024-07-24T11:22:20+00:00" 134 | }, 135 | { 136 | "name": "guzzlehttp/promises", 137 | "version": "2.0.4", 138 | "source": { 139 | "type": "git", 140 | "url": "https://github.com/guzzle/promises.git", 141 | "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" 142 | }, 143 | "dist": { 144 | "type": "zip", 145 | "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", 146 | "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", 147 | "shasum": "" 148 | }, 149 | "require": { 150 | "php": "^7.2.5 || ^8.0" 151 | }, 152 | "require-dev": { 153 | "bamarni/composer-bin-plugin": "^1.8.2", 154 | "phpunit/phpunit": "^8.5.39 || ^9.6.20" 155 | }, 156 | "type": "library", 157 | "extra": { 158 | "bamarni-bin": { 159 | "bin-links": true, 160 | "forward-command": false 161 | } 162 | }, 163 | "autoload": { 164 | "psr-4": { 165 | "GuzzleHttp\\Promise\\": "src/" 166 | } 167 | }, 168 | "notification-url": "https://packagist.org/downloads/", 169 | "license": [ 170 | "MIT" 171 | ], 172 | "authors": [ 173 | { 174 | "name": "Graham Campbell", 175 | "email": "hello@gjcampbell.co.uk", 176 | "homepage": "https://github.com/GrahamCampbell" 177 | }, 178 | { 179 | "name": "Michael Dowling", 180 | "email": "mtdowling@gmail.com", 181 | "homepage": "https://github.com/mtdowling" 182 | }, 183 | { 184 | "name": "Tobias Nyholm", 185 | "email": "tobias.nyholm@gmail.com", 186 | "homepage": "https://github.com/Nyholm" 187 | }, 188 | { 189 | "name": "Tobias Schultze", 190 | "email": "webmaster@tubo-world.de", 191 | "homepage": "https://github.com/Tobion" 192 | } 193 | ], 194 | "description": "Guzzle promises library", 195 | "keywords": [ 196 | "promise" 197 | ], 198 | "support": { 199 | "issues": "https://github.com/guzzle/promises/issues", 200 | "source": "https://github.com/guzzle/promises/tree/2.0.4" 201 | }, 202 | "funding": [ 203 | { 204 | "url": "https://github.com/GrahamCampbell", 205 | "type": "github" 206 | }, 207 | { 208 | "url": "https://github.com/Nyholm", 209 | "type": "github" 210 | }, 211 | { 212 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", 213 | "type": "tidelift" 214 | } 215 | ], 216 | "time": "2024-10-17T10:06:22+00:00" 217 | }, 218 | { 219 | "name": "guzzlehttp/psr7", 220 | "version": "2.7.0", 221 | "source": { 222 | "type": "git", 223 | "url": "https://github.com/guzzle/psr7.git", 224 | "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" 225 | }, 226 | "dist": { 227 | "type": "zip", 228 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", 229 | "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", 230 | "shasum": "" 231 | }, 232 | "require": { 233 | "php": "^7.2.5 || ^8.0", 234 | "psr/http-factory": "^1.0", 235 | "psr/http-message": "^1.1 || ^2.0", 236 | "ralouphie/getallheaders": "^3.0" 237 | }, 238 | "provide": { 239 | "psr/http-factory-implementation": "1.0", 240 | "psr/http-message-implementation": "1.0" 241 | }, 242 | "require-dev": { 243 | "bamarni/composer-bin-plugin": "^1.8.2", 244 | "http-interop/http-factory-tests": "0.9.0", 245 | "phpunit/phpunit": "^8.5.39 || ^9.6.20" 246 | }, 247 | "suggest": { 248 | "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" 249 | }, 250 | "type": "library", 251 | "extra": { 252 | "bamarni-bin": { 253 | "bin-links": true, 254 | "forward-command": false 255 | } 256 | }, 257 | "autoload": { 258 | "psr-4": { 259 | "GuzzleHttp\\Psr7\\": "src/" 260 | } 261 | }, 262 | "notification-url": "https://packagist.org/downloads/", 263 | "license": [ 264 | "MIT" 265 | ], 266 | "authors": [ 267 | { 268 | "name": "Graham Campbell", 269 | "email": "hello@gjcampbell.co.uk", 270 | "homepage": "https://github.com/GrahamCampbell" 271 | }, 272 | { 273 | "name": "Michael Dowling", 274 | "email": "mtdowling@gmail.com", 275 | "homepage": "https://github.com/mtdowling" 276 | }, 277 | { 278 | "name": "George Mponos", 279 | "email": "gmponos@gmail.com", 280 | "homepage": "https://github.com/gmponos" 281 | }, 282 | { 283 | "name": "Tobias Nyholm", 284 | "email": "tobias.nyholm@gmail.com", 285 | "homepage": "https://github.com/Nyholm" 286 | }, 287 | { 288 | "name": "Márk Sági-Kazár", 289 | "email": "mark.sagikazar@gmail.com", 290 | "homepage": "https://github.com/sagikazarmark" 291 | }, 292 | { 293 | "name": "Tobias Schultze", 294 | "email": "webmaster@tubo-world.de", 295 | "homepage": "https://github.com/Tobion" 296 | }, 297 | { 298 | "name": "Márk Sági-Kazár", 299 | "email": "mark.sagikazar@gmail.com", 300 | "homepage": "https://sagikazarmark.hu" 301 | } 302 | ], 303 | "description": "PSR-7 message implementation that also provides common utility methods", 304 | "keywords": [ 305 | "http", 306 | "message", 307 | "psr-7", 308 | "request", 309 | "response", 310 | "stream", 311 | "uri", 312 | "url" 313 | ], 314 | "support": { 315 | "issues": "https://github.com/guzzle/psr7/issues", 316 | "source": "https://github.com/guzzle/psr7/tree/2.7.0" 317 | }, 318 | "funding": [ 319 | { 320 | "url": "https://github.com/GrahamCampbell", 321 | "type": "github" 322 | }, 323 | { 324 | "url": "https://github.com/Nyholm", 325 | "type": "github" 326 | }, 327 | { 328 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", 329 | "type": "tidelift" 330 | } 331 | ], 332 | "time": "2024-07-18T11:15:46+00:00" 333 | }, 334 | { 335 | "name": "league/oauth2-client", 336 | "version": "2.8.0", 337 | "source": { 338 | "type": "git", 339 | "url": "https://github.com/thephpleague/oauth2-client.git", 340 | "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13" 341 | }, 342 | "dist": { 343 | "type": "zip", 344 | "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/3d5cf8d0543731dfb725ab30e4d7289891991e13", 345 | "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13", 346 | "shasum": "" 347 | }, 348 | "require": { 349 | "ext-json": "*", 350 | "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", 351 | "php": "^7.1 || >=8.0.0 <8.5.0" 352 | }, 353 | "require-dev": { 354 | "mockery/mockery": "^1.3.5", 355 | "php-parallel-lint/php-parallel-lint": "^1.4", 356 | "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", 357 | "squizlabs/php_codesniffer": "^3.11" 358 | }, 359 | "type": "library", 360 | "autoload": { 361 | "psr-4": { 362 | "League\\OAuth2\\Client\\": "src/" 363 | } 364 | }, 365 | "notification-url": "https://packagist.org/downloads/", 366 | "license": [ 367 | "MIT" 368 | ], 369 | "authors": [ 370 | { 371 | "name": "Alex Bilbie", 372 | "email": "hello@alexbilbie.com", 373 | "homepage": "http://www.alexbilbie.com", 374 | "role": "Developer" 375 | }, 376 | { 377 | "name": "Woody Gilk", 378 | "homepage": "https://github.com/shadowhand", 379 | "role": "Contributor" 380 | } 381 | ], 382 | "description": "OAuth 2.0 Client Library", 383 | "keywords": [ 384 | "Authentication", 385 | "SSO", 386 | "authorization", 387 | "identity", 388 | "idp", 389 | "oauth", 390 | "oauth2", 391 | "single sign on" 392 | ], 393 | "support": { 394 | "issues": "https://github.com/thephpleague/oauth2-client/issues", 395 | "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.0" 396 | }, 397 | "time": "2024-12-11T05:05:52+00:00" 398 | }, 399 | { 400 | "name": "psr/http-client", 401 | "version": "1.0.3", 402 | "source": { 403 | "type": "git", 404 | "url": "https://github.com/php-fig/http-client.git", 405 | "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" 406 | }, 407 | "dist": { 408 | "type": "zip", 409 | "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", 410 | "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", 411 | "shasum": "" 412 | }, 413 | "require": { 414 | "php": "^7.0 || ^8.0", 415 | "psr/http-message": "^1.0 || ^2.0" 416 | }, 417 | "type": "library", 418 | "extra": { 419 | "branch-alias": { 420 | "dev-master": "1.0.x-dev" 421 | } 422 | }, 423 | "autoload": { 424 | "psr-4": { 425 | "Psr\\Http\\Client\\": "src/" 426 | } 427 | }, 428 | "notification-url": "https://packagist.org/downloads/", 429 | "license": [ 430 | "MIT" 431 | ], 432 | "authors": [ 433 | { 434 | "name": "PHP-FIG", 435 | "homepage": "https://www.php-fig.org/" 436 | } 437 | ], 438 | "description": "Common interface for HTTP clients", 439 | "homepage": "https://github.com/php-fig/http-client", 440 | "keywords": [ 441 | "http", 442 | "http-client", 443 | "psr", 444 | "psr-18" 445 | ], 446 | "support": { 447 | "source": "https://github.com/php-fig/http-client" 448 | }, 449 | "time": "2023-09-23T14:17:50+00:00" 450 | }, 451 | { 452 | "name": "psr/http-factory", 453 | "version": "1.1.0", 454 | "source": { 455 | "type": "git", 456 | "url": "https://github.com/php-fig/http-factory.git", 457 | "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" 458 | }, 459 | "dist": { 460 | "type": "zip", 461 | "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", 462 | "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", 463 | "shasum": "" 464 | }, 465 | "require": { 466 | "php": ">=7.1", 467 | "psr/http-message": "^1.0 || ^2.0" 468 | }, 469 | "type": "library", 470 | "extra": { 471 | "branch-alias": { 472 | "dev-master": "1.0.x-dev" 473 | } 474 | }, 475 | "autoload": { 476 | "psr-4": { 477 | "Psr\\Http\\Message\\": "src/" 478 | } 479 | }, 480 | "notification-url": "https://packagist.org/downloads/", 481 | "license": [ 482 | "MIT" 483 | ], 484 | "authors": [ 485 | { 486 | "name": "PHP-FIG", 487 | "homepage": "https://www.php-fig.org/" 488 | } 489 | ], 490 | "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", 491 | "keywords": [ 492 | "factory", 493 | "http", 494 | "message", 495 | "psr", 496 | "psr-17", 497 | "psr-7", 498 | "request", 499 | "response" 500 | ], 501 | "support": { 502 | "source": "https://github.com/php-fig/http-factory" 503 | }, 504 | "time": "2024-04-15T12:06:14+00:00" 505 | }, 506 | { 507 | "name": "psr/http-message", 508 | "version": "2.0", 509 | "source": { 510 | "type": "git", 511 | "url": "https://github.com/php-fig/http-message.git", 512 | "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" 513 | }, 514 | "dist": { 515 | "type": "zip", 516 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", 517 | "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", 518 | "shasum": "" 519 | }, 520 | "require": { 521 | "php": "^7.2 || ^8.0" 522 | }, 523 | "type": "library", 524 | "extra": { 525 | "branch-alias": { 526 | "dev-master": "2.0.x-dev" 527 | } 528 | }, 529 | "autoload": { 530 | "psr-4": { 531 | "Psr\\Http\\Message\\": "src/" 532 | } 533 | }, 534 | "notification-url": "https://packagist.org/downloads/", 535 | "license": [ 536 | "MIT" 537 | ], 538 | "authors": [ 539 | { 540 | "name": "PHP-FIG", 541 | "homepage": "https://www.php-fig.org/" 542 | } 543 | ], 544 | "description": "Common interface for HTTP messages", 545 | "homepage": "https://github.com/php-fig/http-message", 546 | "keywords": [ 547 | "http", 548 | "http-message", 549 | "psr", 550 | "psr-7", 551 | "request", 552 | "response" 553 | ], 554 | "support": { 555 | "source": "https://github.com/php-fig/http-message/tree/2.0" 556 | }, 557 | "time": "2023-04-04T09:54:51+00:00" 558 | }, 559 | { 560 | "name": "ralouphie/getallheaders", 561 | "version": "3.0.3", 562 | "source": { 563 | "type": "git", 564 | "url": "https://github.com/ralouphie/getallheaders.git", 565 | "reference": "120b605dfeb996808c31b6477290a714d356e822" 566 | }, 567 | "dist": { 568 | "type": "zip", 569 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", 570 | "reference": "120b605dfeb996808c31b6477290a714d356e822", 571 | "shasum": "" 572 | }, 573 | "require": { 574 | "php": ">=5.6" 575 | }, 576 | "require-dev": { 577 | "php-coveralls/php-coveralls": "^2.1", 578 | "phpunit/phpunit": "^5 || ^6.5" 579 | }, 580 | "type": "library", 581 | "autoload": { 582 | "files": [ 583 | "src/getallheaders.php" 584 | ] 585 | }, 586 | "notification-url": "https://packagist.org/downloads/", 587 | "license": [ 588 | "MIT" 589 | ], 590 | "authors": [ 591 | { 592 | "name": "Ralph Khattar", 593 | "email": "ralph.khattar@gmail.com" 594 | } 595 | ], 596 | "description": "A polyfill for getallheaders.", 597 | "support": { 598 | "issues": "https://github.com/ralouphie/getallheaders/issues", 599 | "source": "https://github.com/ralouphie/getallheaders/tree/develop" 600 | }, 601 | "time": "2019-03-08T08:55:37+00:00" 602 | }, 603 | { 604 | "name": "symfony/deprecation-contracts", 605 | "version": "v3.5.1", 606 | "source": { 607 | "type": "git", 608 | "url": "https://github.com/symfony/deprecation-contracts.git", 609 | "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" 610 | }, 611 | "dist": { 612 | "type": "zip", 613 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", 614 | "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", 615 | "shasum": "" 616 | }, 617 | "require": { 618 | "php": ">=8.1" 619 | }, 620 | "type": "library", 621 | "extra": { 622 | "branch-alias": { 623 | "dev-main": "3.5-dev" 624 | }, 625 | "thanks": { 626 | "name": "symfony/contracts", 627 | "url": "https://github.com/symfony/contracts" 628 | } 629 | }, 630 | "autoload": { 631 | "files": [ 632 | "function.php" 633 | ] 634 | }, 635 | "notification-url": "https://packagist.org/downloads/", 636 | "license": [ 637 | "MIT" 638 | ], 639 | "authors": [ 640 | { 641 | "name": "Nicolas Grekas", 642 | "email": "p@tchwork.com" 643 | }, 644 | { 645 | "name": "Symfony Community", 646 | "homepage": "https://symfony.com/contributors" 647 | } 648 | ], 649 | "description": "A generic function and convention to trigger deprecation notices", 650 | "homepage": "https://symfony.com", 651 | "support": { 652 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" 653 | }, 654 | "funding": [ 655 | { 656 | "url": "https://symfony.com/sponsor", 657 | "type": "custom" 658 | }, 659 | { 660 | "url": "https://github.com/fabpot", 661 | "type": "github" 662 | }, 663 | { 664 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 665 | "type": "tidelift" 666 | } 667 | ], 668 | "time": "2024-09-25T14:20:29+00:00" 669 | } 670 | ], 671 | "packages-dev": [ 672 | { 673 | "name": "myclabs/deep-copy", 674 | "version": "1.12.1", 675 | "source": { 676 | "type": "git", 677 | "url": "https://github.com/myclabs/DeepCopy.git", 678 | "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" 679 | }, 680 | "dist": { 681 | "type": "zip", 682 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", 683 | "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", 684 | "shasum": "" 685 | }, 686 | "require": { 687 | "php": "^7.1 || ^8.0" 688 | }, 689 | "conflict": { 690 | "doctrine/collections": "<1.6.8", 691 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 692 | }, 693 | "require-dev": { 694 | "doctrine/collections": "^1.6.8", 695 | "doctrine/common": "^2.13.3 || ^3.2.2", 696 | "phpspec/prophecy": "^1.10", 697 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 698 | }, 699 | "type": "library", 700 | "autoload": { 701 | "files": [ 702 | "src/DeepCopy/deep_copy.php" 703 | ], 704 | "psr-4": { 705 | "DeepCopy\\": "src/DeepCopy/" 706 | } 707 | }, 708 | "notification-url": "https://packagist.org/downloads/", 709 | "license": [ 710 | "MIT" 711 | ], 712 | "description": "Create deep copies (clones) of your objects", 713 | "keywords": [ 714 | "clone", 715 | "copy", 716 | "duplicate", 717 | "object", 718 | "object graph" 719 | ], 720 | "support": { 721 | "issues": "https://github.com/myclabs/DeepCopy/issues", 722 | "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" 723 | }, 724 | "funding": [ 725 | { 726 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 727 | "type": "tidelift" 728 | } 729 | ], 730 | "time": "2024-11-08T17:47:46+00:00" 731 | }, 732 | { 733 | "name": "nikic/php-parser", 734 | "version": "v5.3.1", 735 | "source": { 736 | "type": "git", 737 | "url": "https://github.com/nikic/PHP-Parser.git", 738 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" 739 | }, 740 | "dist": { 741 | "type": "zip", 742 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", 743 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", 744 | "shasum": "" 745 | }, 746 | "require": { 747 | "ext-ctype": "*", 748 | "ext-json": "*", 749 | "ext-tokenizer": "*", 750 | "php": ">=7.4" 751 | }, 752 | "require-dev": { 753 | "ircmaxell/php-yacc": "^0.0.7", 754 | "phpunit/phpunit": "^9.0" 755 | }, 756 | "bin": [ 757 | "bin/php-parse" 758 | ], 759 | "type": "library", 760 | "extra": { 761 | "branch-alias": { 762 | "dev-master": "5.0-dev" 763 | } 764 | }, 765 | "autoload": { 766 | "psr-4": { 767 | "PhpParser\\": "lib/PhpParser" 768 | } 769 | }, 770 | "notification-url": "https://packagist.org/downloads/", 771 | "license": [ 772 | "BSD-3-Clause" 773 | ], 774 | "authors": [ 775 | { 776 | "name": "Nikita Popov" 777 | } 778 | ], 779 | "description": "A PHP parser written in PHP", 780 | "keywords": [ 781 | "parser", 782 | "php" 783 | ], 784 | "support": { 785 | "issues": "https://github.com/nikic/PHP-Parser/issues", 786 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" 787 | }, 788 | "time": "2024-10-08T18:51:32+00:00" 789 | }, 790 | { 791 | "name": "phar-io/manifest", 792 | "version": "2.0.4", 793 | "source": { 794 | "type": "git", 795 | "url": "https://github.com/phar-io/manifest.git", 796 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 797 | }, 798 | "dist": { 799 | "type": "zip", 800 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 801 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 802 | "shasum": "" 803 | }, 804 | "require": { 805 | "ext-dom": "*", 806 | "ext-libxml": "*", 807 | "ext-phar": "*", 808 | "ext-xmlwriter": "*", 809 | "phar-io/version": "^3.0.1", 810 | "php": "^7.2 || ^8.0" 811 | }, 812 | "type": "library", 813 | "extra": { 814 | "branch-alias": { 815 | "dev-master": "2.0.x-dev" 816 | } 817 | }, 818 | "autoload": { 819 | "classmap": [ 820 | "src/" 821 | ] 822 | }, 823 | "notification-url": "https://packagist.org/downloads/", 824 | "license": [ 825 | "BSD-3-Clause" 826 | ], 827 | "authors": [ 828 | { 829 | "name": "Arne Blankerts", 830 | "email": "arne@blankerts.de", 831 | "role": "Developer" 832 | }, 833 | { 834 | "name": "Sebastian Heuer", 835 | "email": "sebastian@phpeople.de", 836 | "role": "Developer" 837 | }, 838 | { 839 | "name": "Sebastian Bergmann", 840 | "email": "sebastian@phpunit.de", 841 | "role": "Developer" 842 | } 843 | ], 844 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 845 | "support": { 846 | "issues": "https://github.com/phar-io/manifest/issues", 847 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 848 | }, 849 | "funding": [ 850 | { 851 | "url": "https://github.com/theseer", 852 | "type": "github" 853 | } 854 | ], 855 | "time": "2024-03-03T12:33:53+00:00" 856 | }, 857 | { 858 | "name": "phar-io/version", 859 | "version": "3.2.1", 860 | "source": { 861 | "type": "git", 862 | "url": "https://github.com/phar-io/version.git", 863 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 864 | }, 865 | "dist": { 866 | "type": "zip", 867 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 868 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 869 | "shasum": "" 870 | }, 871 | "require": { 872 | "php": "^7.2 || ^8.0" 873 | }, 874 | "type": "library", 875 | "autoload": { 876 | "classmap": [ 877 | "src/" 878 | ] 879 | }, 880 | "notification-url": "https://packagist.org/downloads/", 881 | "license": [ 882 | "BSD-3-Clause" 883 | ], 884 | "authors": [ 885 | { 886 | "name": "Arne Blankerts", 887 | "email": "arne@blankerts.de", 888 | "role": "Developer" 889 | }, 890 | { 891 | "name": "Sebastian Heuer", 892 | "email": "sebastian@phpeople.de", 893 | "role": "Developer" 894 | }, 895 | { 896 | "name": "Sebastian Bergmann", 897 | "email": "sebastian@phpunit.de", 898 | "role": "Developer" 899 | } 900 | ], 901 | "description": "Library for handling version information and constraints", 902 | "support": { 903 | "issues": "https://github.com/phar-io/version/issues", 904 | "source": "https://github.com/phar-io/version/tree/3.2.1" 905 | }, 906 | "time": "2022-02-21T01:04:05+00:00" 907 | }, 908 | { 909 | "name": "phpunit/php-code-coverage", 910 | "version": "11.0.8", 911 | "source": { 912 | "type": "git", 913 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 914 | "reference": "418c59fd080954f8c4aa5631d9502ecda2387118" 915 | }, 916 | "dist": { 917 | "type": "zip", 918 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/418c59fd080954f8c4aa5631d9502ecda2387118", 919 | "reference": "418c59fd080954f8c4aa5631d9502ecda2387118", 920 | "shasum": "" 921 | }, 922 | "require": { 923 | "ext-dom": "*", 924 | "ext-libxml": "*", 925 | "ext-xmlwriter": "*", 926 | "nikic/php-parser": "^5.3.1", 927 | "php": ">=8.2", 928 | "phpunit/php-file-iterator": "^5.1.0", 929 | "phpunit/php-text-template": "^4.0.1", 930 | "sebastian/code-unit-reverse-lookup": "^4.0.1", 931 | "sebastian/complexity": "^4.0.1", 932 | "sebastian/environment": "^7.2.0", 933 | "sebastian/lines-of-code": "^3.0.1", 934 | "sebastian/version": "^5.0.2", 935 | "theseer/tokenizer": "^1.2.3" 936 | }, 937 | "require-dev": { 938 | "phpunit/phpunit": "^11.5.0" 939 | }, 940 | "suggest": { 941 | "ext-pcov": "PHP extension that provides line coverage", 942 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 943 | }, 944 | "type": "library", 945 | "extra": { 946 | "branch-alias": { 947 | "dev-main": "11.0.x-dev" 948 | } 949 | }, 950 | "autoload": { 951 | "classmap": [ 952 | "src/" 953 | ] 954 | }, 955 | "notification-url": "https://packagist.org/downloads/", 956 | "license": [ 957 | "BSD-3-Clause" 958 | ], 959 | "authors": [ 960 | { 961 | "name": "Sebastian Bergmann", 962 | "email": "sebastian@phpunit.de", 963 | "role": "lead" 964 | } 965 | ], 966 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 967 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 968 | "keywords": [ 969 | "coverage", 970 | "testing", 971 | "xunit" 972 | ], 973 | "support": { 974 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 975 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 976 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.8" 977 | }, 978 | "funding": [ 979 | { 980 | "url": "https://github.com/sebastianbergmann", 981 | "type": "github" 982 | } 983 | ], 984 | "time": "2024-12-11T12:34:27+00:00" 985 | }, 986 | { 987 | "name": "phpunit/php-file-iterator", 988 | "version": "5.1.0", 989 | "source": { 990 | "type": "git", 991 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 992 | "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" 993 | }, 994 | "dist": { 995 | "type": "zip", 996 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", 997 | "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", 998 | "shasum": "" 999 | }, 1000 | "require": { 1001 | "php": ">=8.2" 1002 | }, 1003 | "require-dev": { 1004 | "phpunit/phpunit": "^11.0" 1005 | }, 1006 | "type": "library", 1007 | "extra": { 1008 | "branch-alias": { 1009 | "dev-main": "5.0-dev" 1010 | } 1011 | }, 1012 | "autoload": { 1013 | "classmap": [ 1014 | "src/" 1015 | ] 1016 | }, 1017 | "notification-url": "https://packagist.org/downloads/", 1018 | "license": [ 1019 | "BSD-3-Clause" 1020 | ], 1021 | "authors": [ 1022 | { 1023 | "name": "Sebastian Bergmann", 1024 | "email": "sebastian@phpunit.de", 1025 | "role": "lead" 1026 | } 1027 | ], 1028 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 1029 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 1030 | "keywords": [ 1031 | "filesystem", 1032 | "iterator" 1033 | ], 1034 | "support": { 1035 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 1036 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", 1037 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" 1038 | }, 1039 | "funding": [ 1040 | { 1041 | "url": "https://github.com/sebastianbergmann", 1042 | "type": "github" 1043 | } 1044 | ], 1045 | "time": "2024-08-27T05:02:59+00:00" 1046 | }, 1047 | { 1048 | "name": "phpunit/php-invoker", 1049 | "version": "5.0.1", 1050 | "source": { 1051 | "type": "git", 1052 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 1053 | "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" 1054 | }, 1055 | "dist": { 1056 | "type": "zip", 1057 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", 1058 | "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", 1059 | "shasum": "" 1060 | }, 1061 | "require": { 1062 | "php": ">=8.2" 1063 | }, 1064 | "require-dev": { 1065 | "ext-pcntl": "*", 1066 | "phpunit/phpunit": "^11.0" 1067 | }, 1068 | "suggest": { 1069 | "ext-pcntl": "*" 1070 | }, 1071 | "type": "library", 1072 | "extra": { 1073 | "branch-alias": { 1074 | "dev-main": "5.0-dev" 1075 | } 1076 | }, 1077 | "autoload": { 1078 | "classmap": [ 1079 | "src/" 1080 | ] 1081 | }, 1082 | "notification-url": "https://packagist.org/downloads/", 1083 | "license": [ 1084 | "BSD-3-Clause" 1085 | ], 1086 | "authors": [ 1087 | { 1088 | "name": "Sebastian Bergmann", 1089 | "email": "sebastian@phpunit.de", 1090 | "role": "lead" 1091 | } 1092 | ], 1093 | "description": "Invoke callables with a timeout", 1094 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 1095 | "keywords": [ 1096 | "process" 1097 | ], 1098 | "support": { 1099 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 1100 | "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", 1101 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" 1102 | }, 1103 | "funding": [ 1104 | { 1105 | "url": "https://github.com/sebastianbergmann", 1106 | "type": "github" 1107 | } 1108 | ], 1109 | "time": "2024-07-03T05:07:44+00:00" 1110 | }, 1111 | { 1112 | "name": "phpunit/php-text-template", 1113 | "version": "4.0.1", 1114 | "source": { 1115 | "type": "git", 1116 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 1117 | "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" 1118 | }, 1119 | "dist": { 1120 | "type": "zip", 1121 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", 1122 | "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", 1123 | "shasum": "" 1124 | }, 1125 | "require": { 1126 | "php": ">=8.2" 1127 | }, 1128 | "require-dev": { 1129 | "phpunit/phpunit": "^11.0" 1130 | }, 1131 | "type": "library", 1132 | "extra": { 1133 | "branch-alias": { 1134 | "dev-main": "4.0-dev" 1135 | } 1136 | }, 1137 | "autoload": { 1138 | "classmap": [ 1139 | "src/" 1140 | ] 1141 | }, 1142 | "notification-url": "https://packagist.org/downloads/", 1143 | "license": [ 1144 | "BSD-3-Clause" 1145 | ], 1146 | "authors": [ 1147 | { 1148 | "name": "Sebastian Bergmann", 1149 | "email": "sebastian@phpunit.de", 1150 | "role": "lead" 1151 | } 1152 | ], 1153 | "description": "Simple template engine.", 1154 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 1155 | "keywords": [ 1156 | "template" 1157 | ], 1158 | "support": { 1159 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 1160 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", 1161 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" 1162 | }, 1163 | "funding": [ 1164 | { 1165 | "url": "https://github.com/sebastianbergmann", 1166 | "type": "github" 1167 | } 1168 | ], 1169 | "time": "2024-07-03T05:08:43+00:00" 1170 | }, 1171 | { 1172 | "name": "phpunit/php-timer", 1173 | "version": "7.0.1", 1174 | "source": { 1175 | "type": "git", 1176 | "url": "https://github.com/sebastianbergmann/php-timer.git", 1177 | "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" 1178 | }, 1179 | "dist": { 1180 | "type": "zip", 1181 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", 1182 | "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", 1183 | "shasum": "" 1184 | }, 1185 | "require": { 1186 | "php": ">=8.2" 1187 | }, 1188 | "require-dev": { 1189 | "phpunit/phpunit": "^11.0" 1190 | }, 1191 | "type": "library", 1192 | "extra": { 1193 | "branch-alias": { 1194 | "dev-main": "7.0-dev" 1195 | } 1196 | }, 1197 | "autoload": { 1198 | "classmap": [ 1199 | "src/" 1200 | ] 1201 | }, 1202 | "notification-url": "https://packagist.org/downloads/", 1203 | "license": [ 1204 | "BSD-3-Clause" 1205 | ], 1206 | "authors": [ 1207 | { 1208 | "name": "Sebastian Bergmann", 1209 | "email": "sebastian@phpunit.de", 1210 | "role": "lead" 1211 | } 1212 | ], 1213 | "description": "Utility class for timing", 1214 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1215 | "keywords": [ 1216 | "timer" 1217 | ], 1218 | "support": { 1219 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 1220 | "security": "https://github.com/sebastianbergmann/php-timer/security/policy", 1221 | "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" 1222 | }, 1223 | "funding": [ 1224 | { 1225 | "url": "https://github.com/sebastianbergmann", 1226 | "type": "github" 1227 | } 1228 | ], 1229 | "time": "2024-07-03T05:09:35+00:00" 1230 | }, 1231 | { 1232 | "name": "phpunit/phpunit", 1233 | "version": "11.5.1", 1234 | "source": { 1235 | "type": "git", 1236 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1237 | "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a" 1238 | }, 1239 | "dist": { 1240 | "type": "zip", 1241 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2b94d4f2450b9869fa64a46fd8a6a41997aef56a", 1242 | "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a", 1243 | "shasum": "" 1244 | }, 1245 | "require": { 1246 | "ext-dom": "*", 1247 | "ext-json": "*", 1248 | "ext-libxml": "*", 1249 | "ext-mbstring": "*", 1250 | "ext-xml": "*", 1251 | "ext-xmlwriter": "*", 1252 | "myclabs/deep-copy": "^1.12.1", 1253 | "phar-io/manifest": "^2.0.4", 1254 | "phar-io/version": "^3.2.1", 1255 | "php": ">=8.2", 1256 | "phpunit/php-code-coverage": "^11.0.7", 1257 | "phpunit/php-file-iterator": "^5.1.0", 1258 | "phpunit/php-invoker": "^5.0.1", 1259 | "phpunit/php-text-template": "^4.0.1", 1260 | "phpunit/php-timer": "^7.0.1", 1261 | "sebastian/cli-parser": "^3.0.2", 1262 | "sebastian/code-unit": "^3.0.1", 1263 | "sebastian/comparator": "^6.2.1", 1264 | "sebastian/diff": "^6.0.2", 1265 | "sebastian/environment": "^7.2.0", 1266 | "sebastian/exporter": "^6.3.0", 1267 | "sebastian/global-state": "^7.0.2", 1268 | "sebastian/object-enumerator": "^6.0.1", 1269 | "sebastian/type": "^5.1.0", 1270 | "sebastian/version": "^5.0.2", 1271 | "staabm/side-effects-detector": "^1.0.5" 1272 | }, 1273 | "suggest": { 1274 | "ext-soap": "To be able to generate mocks based on WSDL files" 1275 | }, 1276 | "bin": [ 1277 | "phpunit" 1278 | ], 1279 | "type": "library", 1280 | "extra": { 1281 | "branch-alias": { 1282 | "dev-main": "11.5-dev" 1283 | } 1284 | }, 1285 | "autoload": { 1286 | "files": [ 1287 | "src/Framework/Assert/Functions.php" 1288 | ], 1289 | "classmap": [ 1290 | "src/" 1291 | ] 1292 | }, 1293 | "notification-url": "https://packagist.org/downloads/", 1294 | "license": [ 1295 | "BSD-3-Clause" 1296 | ], 1297 | "authors": [ 1298 | { 1299 | "name": "Sebastian Bergmann", 1300 | "email": "sebastian@phpunit.de", 1301 | "role": "lead" 1302 | } 1303 | ], 1304 | "description": "The PHP Unit Testing framework.", 1305 | "homepage": "https://phpunit.de/", 1306 | "keywords": [ 1307 | "phpunit", 1308 | "testing", 1309 | "xunit" 1310 | ], 1311 | "support": { 1312 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 1313 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 1314 | "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.1" 1315 | }, 1316 | "funding": [ 1317 | { 1318 | "url": "https://phpunit.de/sponsors.html", 1319 | "type": "custom" 1320 | }, 1321 | { 1322 | "url": "https://github.com/sebastianbergmann", 1323 | "type": "github" 1324 | }, 1325 | { 1326 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 1327 | "type": "tidelift" 1328 | } 1329 | ], 1330 | "time": "2024-12-11T10:52:48+00:00" 1331 | }, 1332 | { 1333 | "name": "sebastian/cli-parser", 1334 | "version": "3.0.2", 1335 | "source": { 1336 | "type": "git", 1337 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 1338 | "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" 1339 | }, 1340 | "dist": { 1341 | "type": "zip", 1342 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", 1343 | "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", 1344 | "shasum": "" 1345 | }, 1346 | "require": { 1347 | "php": ">=8.2" 1348 | }, 1349 | "require-dev": { 1350 | "phpunit/phpunit": "^11.0" 1351 | }, 1352 | "type": "library", 1353 | "extra": { 1354 | "branch-alias": { 1355 | "dev-main": "3.0-dev" 1356 | } 1357 | }, 1358 | "autoload": { 1359 | "classmap": [ 1360 | "src/" 1361 | ] 1362 | }, 1363 | "notification-url": "https://packagist.org/downloads/", 1364 | "license": [ 1365 | "BSD-3-Clause" 1366 | ], 1367 | "authors": [ 1368 | { 1369 | "name": "Sebastian Bergmann", 1370 | "email": "sebastian@phpunit.de", 1371 | "role": "lead" 1372 | } 1373 | ], 1374 | "description": "Library for parsing CLI options", 1375 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 1376 | "support": { 1377 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 1378 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", 1379 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" 1380 | }, 1381 | "funding": [ 1382 | { 1383 | "url": "https://github.com/sebastianbergmann", 1384 | "type": "github" 1385 | } 1386 | ], 1387 | "time": "2024-07-03T04:41:36+00:00" 1388 | }, 1389 | { 1390 | "name": "sebastian/code-unit", 1391 | "version": "3.0.2", 1392 | "source": { 1393 | "type": "git", 1394 | "url": "https://github.com/sebastianbergmann/code-unit.git", 1395 | "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" 1396 | }, 1397 | "dist": { 1398 | "type": "zip", 1399 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", 1400 | "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", 1401 | "shasum": "" 1402 | }, 1403 | "require": { 1404 | "php": ">=8.2" 1405 | }, 1406 | "require-dev": { 1407 | "phpunit/phpunit": "^11.5" 1408 | }, 1409 | "type": "library", 1410 | "extra": { 1411 | "branch-alias": { 1412 | "dev-main": "3.0-dev" 1413 | } 1414 | }, 1415 | "autoload": { 1416 | "classmap": [ 1417 | "src/" 1418 | ] 1419 | }, 1420 | "notification-url": "https://packagist.org/downloads/", 1421 | "license": [ 1422 | "BSD-3-Clause" 1423 | ], 1424 | "authors": [ 1425 | { 1426 | "name": "Sebastian Bergmann", 1427 | "email": "sebastian@phpunit.de", 1428 | "role": "lead" 1429 | } 1430 | ], 1431 | "description": "Collection of value objects that represent the PHP code units", 1432 | "homepage": "https://github.com/sebastianbergmann/code-unit", 1433 | "support": { 1434 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 1435 | "security": "https://github.com/sebastianbergmann/code-unit/security/policy", 1436 | "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" 1437 | }, 1438 | "funding": [ 1439 | { 1440 | "url": "https://github.com/sebastianbergmann", 1441 | "type": "github" 1442 | } 1443 | ], 1444 | "time": "2024-12-12T09:59:06+00:00" 1445 | }, 1446 | { 1447 | "name": "sebastian/code-unit-reverse-lookup", 1448 | "version": "4.0.1", 1449 | "source": { 1450 | "type": "git", 1451 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1452 | "reference": "183a9b2632194febd219bb9246eee421dad8d45e" 1453 | }, 1454 | "dist": { 1455 | "type": "zip", 1456 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", 1457 | "reference": "183a9b2632194febd219bb9246eee421dad8d45e", 1458 | "shasum": "" 1459 | }, 1460 | "require": { 1461 | "php": ">=8.2" 1462 | }, 1463 | "require-dev": { 1464 | "phpunit/phpunit": "^11.0" 1465 | }, 1466 | "type": "library", 1467 | "extra": { 1468 | "branch-alias": { 1469 | "dev-main": "4.0-dev" 1470 | } 1471 | }, 1472 | "autoload": { 1473 | "classmap": [ 1474 | "src/" 1475 | ] 1476 | }, 1477 | "notification-url": "https://packagist.org/downloads/", 1478 | "license": [ 1479 | "BSD-3-Clause" 1480 | ], 1481 | "authors": [ 1482 | { 1483 | "name": "Sebastian Bergmann", 1484 | "email": "sebastian@phpunit.de" 1485 | } 1486 | ], 1487 | "description": "Looks up which function or method a line of code belongs to", 1488 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1489 | "support": { 1490 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 1491 | "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", 1492 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" 1493 | }, 1494 | "funding": [ 1495 | { 1496 | "url": "https://github.com/sebastianbergmann", 1497 | "type": "github" 1498 | } 1499 | ], 1500 | "time": "2024-07-03T04:45:54+00:00" 1501 | }, 1502 | { 1503 | "name": "sebastian/comparator", 1504 | "version": "6.2.1", 1505 | "source": { 1506 | "type": "git", 1507 | "url": "https://github.com/sebastianbergmann/comparator.git", 1508 | "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" 1509 | }, 1510 | "dist": { 1511 | "type": "zip", 1512 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", 1513 | "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", 1514 | "shasum": "" 1515 | }, 1516 | "require": { 1517 | "ext-dom": "*", 1518 | "ext-mbstring": "*", 1519 | "php": ">=8.2", 1520 | "sebastian/diff": "^6.0", 1521 | "sebastian/exporter": "^6.0" 1522 | }, 1523 | "require-dev": { 1524 | "phpunit/phpunit": "^11.4" 1525 | }, 1526 | "type": "library", 1527 | "extra": { 1528 | "branch-alias": { 1529 | "dev-main": "6.2-dev" 1530 | } 1531 | }, 1532 | "autoload": { 1533 | "classmap": [ 1534 | "src/" 1535 | ] 1536 | }, 1537 | "notification-url": "https://packagist.org/downloads/", 1538 | "license": [ 1539 | "BSD-3-Clause" 1540 | ], 1541 | "authors": [ 1542 | { 1543 | "name": "Sebastian Bergmann", 1544 | "email": "sebastian@phpunit.de" 1545 | }, 1546 | { 1547 | "name": "Jeff Welch", 1548 | "email": "whatthejeff@gmail.com" 1549 | }, 1550 | { 1551 | "name": "Volker Dusch", 1552 | "email": "github@wallbash.com" 1553 | }, 1554 | { 1555 | "name": "Bernhard Schussek", 1556 | "email": "bschussek@2bepublished.at" 1557 | } 1558 | ], 1559 | "description": "Provides the functionality to compare PHP values for equality", 1560 | "homepage": "https://github.com/sebastianbergmann/comparator", 1561 | "keywords": [ 1562 | "comparator", 1563 | "compare", 1564 | "equality" 1565 | ], 1566 | "support": { 1567 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1568 | "security": "https://github.com/sebastianbergmann/comparator/security/policy", 1569 | "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" 1570 | }, 1571 | "funding": [ 1572 | { 1573 | "url": "https://github.com/sebastianbergmann", 1574 | "type": "github" 1575 | } 1576 | ], 1577 | "time": "2024-10-31T05:30:08+00:00" 1578 | }, 1579 | { 1580 | "name": "sebastian/complexity", 1581 | "version": "4.0.1", 1582 | "source": { 1583 | "type": "git", 1584 | "url": "https://github.com/sebastianbergmann/complexity.git", 1585 | "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" 1586 | }, 1587 | "dist": { 1588 | "type": "zip", 1589 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", 1590 | "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", 1591 | "shasum": "" 1592 | }, 1593 | "require": { 1594 | "nikic/php-parser": "^5.0", 1595 | "php": ">=8.2" 1596 | }, 1597 | "require-dev": { 1598 | "phpunit/phpunit": "^11.0" 1599 | }, 1600 | "type": "library", 1601 | "extra": { 1602 | "branch-alias": { 1603 | "dev-main": "4.0-dev" 1604 | } 1605 | }, 1606 | "autoload": { 1607 | "classmap": [ 1608 | "src/" 1609 | ] 1610 | }, 1611 | "notification-url": "https://packagist.org/downloads/", 1612 | "license": [ 1613 | "BSD-3-Clause" 1614 | ], 1615 | "authors": [ 1616 | { 1617 | "name": "Sebastian Bergmann", 1618 | "email": "sebastian@phpunit.de", 1619 | "role": "lead" 1620 | } 1621 | ], 1622 | "description": "Library for calculating the complexity of PHP code units", 1623 | "homepage": "https://github.com/sebastianbergmann/complexity", 1624 | "support": { 1625 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1626 | "security": "https://github.com/sebastianbergmann/complexity/security/policy", 1627 | "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" 1628 | }, 1629 | "funding": [ 1630 | { 1631 | "url": "https://github.com/sebastianbergmann", 1632 | "type": "github" 1633 | } 1634 | ], 1635 | "time": "2024-07-03T04:49:50+00:00" 1636 | }, 1637 | { 1638 | "name": "sebastian/diff", 1639 | "version": "6.0.2", 1640 | "source": { 1641 | "type": "git", 1642 | "url": "https://github.com/sebastianbergmann/diff.git", 1643 | "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" 1644 | }, 1645 | "dist": { 1646 | "type": "zip", 1647 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", 1648 | "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", 1649 | "shasum": "" 1650 | }, 1651 | "require": { 1652 | "php": ">=8.2" 1653 | }, 1654 | "require-dev": { 1655 | "phpunit/phpunit": "^11.0", 1656 | "symfony/process": "^4.2 || ^5" 1657 | }, 1658 | "type": "library", 1659 | "extra": { 1660 | "branch-alias": { 1661 | "dev-main": "6.0-dev" 1662 | } 1663 | }, 1664 | "autoload": { 1665 | "classmap": [ 1666 | "src/" 1667 | ] 1668 | }, 1669 | "notification-url": "https://packagist.org/downloads/", 1670 | "license": [ 1671 | "BSD-3-Clause" 1672 | ], 1673 | "authors": [ 1674 | { 1675 | "name": "Sebastian Bergmann", 1676 | "email": "sebastian@phpunit.de" 1677 | }, 1678 | { 1679 | "name": "Kore Nordmann", 1680 | "email": "mail@kore-nordmann.de" 1681 | } 1682 | ], 1683 | "description": "Diff implementation", 1684 | "homepage": "https://github.com/sebastianbergmann/diff", 1685 | "keywords": [ 1686 | "diff", 1687 | "udiff", 1688 | "unidiff", 1689 | "unified diff" 1690 | ], 1691 | "support": { 1692 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1693 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1694 | "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" 1695 | }, 1696 | "funding": [ 1697 | { 1698 | "url": "https://github.com/sebastianbergmann", 1699 | "type": "github" 1700 | } 1701 | ], 1702 | "time": "2024-07-03T04:53:05+00:00" 1703 | }, 1704 | { 1705 | "name": "sebastian/environment", 1706 | "version": "7.2.0", 1707 | "source": { 1708 | "type": "git", 1709 | "url": "https://github.com/sebastianbergmann/environment.git", 1710 | "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" 1711 | }, 1712 | "dist": { 1713 | "type": "zip", 1714 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", 1715 | "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", 1716 | "shasum": "" 1717 | }, 1718 | "require": { 1719 | "php": ">=8.2" 1720 | }, 1721 | "require-dev": { 1722 | "phpunit/phpunit": "^11.0" 1723 | }, 1724 | "suggest": { 1725 | "ext-posix": "*" 1726 | }, 1727 | "type": "library", 1728 | "extra": { 1729 | "branch-alias": { 1730 | "dev-main": "7.2-dev" 1731 | } 1732 | }, 1733 | "autoload": { 1734 | "classmap": [ 1735 | "src/" 1736 | ] 1737 | }, 1738 | "notification-url": "https://packagist.org/downloads/", 1739 | "license": [ 1740 | "BSD-3-Clause" 1741 | ], 1742 | "authors": [ 1743 | { 1744 | "name": "Sebastian Bergmann", 1745 | "email": "sebastian@phpunit.de" 1746 | } 1747 | ], 1748 | "description": "Provides functionality to handle HHVM/PHP environments", 1749 | "homepage": "https://github.com/sebastianbergmann/environment", 1750 | "keywords": [ 1751 | "Xdebug", 1752 | "environment", 1753 | "hhvm" 1754 | ], 1755 | "support": { 1756 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1757 | "security": "https://github.com/sebastianbergmann/environment/security/policy", 1758 | "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" 1759 | }, 1760 | "funding": [ 1761 | { 1762 | "url": "https://github.com/sebastianbergmann", 1763 | "type": "github" 1764 | } 1765 | ], 1766 | "time": "2024-07-03T04:54:44+00:00" 1767 | }, 1768 | { 1769 | "name": "sebastian/exporter", 1770 | "version": "6.3.0", 1771 | "source": { 1772 | "type": "git", 1773 | "url": "https://github.com/sebastianbergmann/exporter.git", 1774 | "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" 1775 | }, 1776 | "dist": { 1777 | "type": "zip", 1778 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", 1779 | "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", 1780 | "shasum": "" 1781 | }, 1782 | "require": { 1783 | "ext-mbstring": "*", 1784 | "php": ">=8.2", 1785 | "sebastian/recursion-context": "^6.0" 1786 | }, 1787 | "require-dev": { 1788 | "phpunit/phpunit": "^11.3" 1789 | }, 1790 | "type": "library", 1791 | "extra": { 1792 | "branch-alias": { 1793 | "dev-main": "6.1-dev" 1794 | } 1795 | }, 1796 | "autoload": { 1797 | "classmap": [ 1798 | "src/" 1799 | ] 1800 | }, 1801 | "notification-url": "https://packagist.org/downloads/", 1802 | "license": [ 1803 | "BSD-3-Clause" 1804 | ], 1805 | "authors": [ 1806 | { 1807 | "name": "Sebastian Bergmann", 1808 | "email": "sebastian@phpunit.de" 1809 | }, 1810 | { 1811 | "name": "Jeff Welch", 1812 | "email": "whatthejeff@gmail.com" 1813 | }, 1814 | { 1815 | "name": "Volker Dusch", 1816 | "email": "github@wallbash.com" 1817 | }, 1818 | { 1819 | "name": "Adam Harvey", 1820 | "email": "aharvey@php.net" 1821 | }, 1822 | { 1823 | "name": "Bernhard Schussek", 1824 | "email": "bschussek@gmail.com" 1825 | } 1826 | ], 1827 | "description": "Provides the functionality to export PHP variables for visualization", 1828 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1829 | "keywords": [ 1830 | "export", 1831 | "exporter" 1832 | ], 1833 | "support": { 1834 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1835 | "security": "https://github.com/sebastianbergmann/exporter/security/policy", 1836 | "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" 1837 | }, 1838 | "funding": [ 1839 | { 1840 | "url": "https://github.com/sebastianbergmann", 1841 | "type": "github" 1842 | } 1843 | ], 1844 | "time": "2024-12-05T09:17:50+00:00" 1845 | }, 1846 | { 1847 | "name": "sebastian/global-state", 1848 | "version": "7.0.2", 1849 | "source": { 1850 | "type": "git", 1851 | "url": "https://github.com/sebastianbergmann/global-state.git", 1852 | "reference": "3be331570a721f9a4b5917f4209773de17f747d7" 1853 | }, 1854 | "dist": { 1855 | "type": "zip", 1856 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", 1857 | "reference": "3be331570a721f9a4b5917f4209773de17f747d7", 1858 | "shasum": "" 1859 | }, 1860 | "require": { 1861 | "php": ">=8.2", 1862 | "sebastian/object-reflector": "^4.0", 1863 | "sebastian/recursion-context": "^6.0" 1864 | }, 1865 | "require-dev": { 1866 | "ext-dom": "*", 1867 | "phpunit/phpunit": "^11.0" 1868 | }, 1869 | "type": "library", 1870 | "extra": { 1871 | "branch-alias": { 1872 | "dev-main": "7.0-dev" 1873 | } 1874 | }, 1875 | "autoload": { 1876 | "classmap": [ 1877 | "src/" 1878 | ] 1879 | }, 1880 | "notification-url": "https://packagist.org/downloads/", 1881 | "license": [ 1882 | "BSD-3-Clause" 1883 | ], 1884 | "authors": [ 1885 | { 1886 | "name": "Sebastian Bergmann", 1887 | "email": "sebastian@phpunit.de" 1888 | } 1889 | ], 1890 | "description": "Snapshotting of global state", 1891 | "homepage": "https://www.github.com/sebastianbergmann/global-state", 1892 | "keywords": [ 1893 | "global state" 1894 | ], 1895 | "support": { 1896 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1897 | "security": "https://github.com/sebastianbergmann/global-state/security/policy", 1898 | "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" 1899 | }, 1900 | "funding": [ 1901 | { 1902 | "url": "https://github.com/sebastianbergmann", 1903 | "type": "github" 1904 | } 1905 | ], 1906 | "time": "2024-07-03T04:57:36+00:00" 1907 | }, 1908 | { 1909 | "name": "sebastian/lines-of-code", 1910 | "version": "3.0.1", 1911 | "source": { 1912 | "type": "git", 1913 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1914 | "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" 1915 | }, 1916 | "dist": { 1917 | "type": "zip", 1918 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", 1919 | "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", 1920 | "shasum": "" 1921 | }, 1922 | "require": { 1923 | "nikic/php-parser": "^5.0", 1924 | "php": ">=8.2" 1925 | }, 1926 | "require-dev": { 1927 | "phpunit/phpunit": "^11.0" 1928 | }, 1929 | "type": "library", 1930 | "extra": { 1931 | "branch-alias": { 1932 | "dev-main": "3.0-dev" 1933 | } 1934 | }, 1935 | "autoload": { 1936 | "classmap": [ 1937 | "src/" 1938 | ] 1939 | }, 1940 | "notification-url": "https://packagist.org/downloads/", 1941 | "license": [ 1942 | "BSD-3-Clause" 1943 | ], 1944 | "authors": [ 1945 | { 1946 | "name": "Sebastian Bergmann", 1947 | "email": "sebastian@phpunit.de", 1948 | "role": "lead" 1949 | } 1950 | ], 1951 | "description": "Library for counting the lines of code in PHP source code", 1952 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1953 | "support": { 1954 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1955 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", 1956 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" 1957 | }, 1958 | "funding": [ 1959 | { 1960 | "url": "https://github.com/sebastianbergmann", 1961 | "type": "github" 1962 | } 1963 | ], 1964 | "time": "2024-07-03T04:58:38+00:00" 1965 | }, 1966 | { 1967 | "name": "sebastian/object-enumerator", 1968 | "version": "6.0.1", 1969 | "source": { 1970 | "type": "git", 1971 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1972 | "reference": "f5b498e631a74204185071eb41f33f38d64608aa" 1973 | }, 1974 | "dist": { 1975 | "type": "zip", 1976 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", 1977 | "reference": "f5b498e631a74204185071eb41f33f38d64608aa", 1978 | "shasum": "" 1979 | }, 1980 | "require": { 1981 | "php": ">=8.2", 1982 | "sebastian/object-reflector": "^4.0", 1983 | "sebastian/recursion-context": "^6.0" 1984 | }, 1985 | "require-dev": { 1986 | "phpunit/phpunit": "^11.0" 1987 | }, 1988 | "type": "library", 1989 | "extra": { 1990 | "branch-alias": { 1991 | "dev-main": "6.0-dev" 1992 | } 1993 | }, 1994 | "autoload": { 1995 | "classmap": [ 1996 | "src/" 1997 | ] 1998 | }, 1999 | "notification-url": "https://packagist.org/downloads/", 2000 | "license": [ 2001 | "BSD-3-Clause" 2002 | ], 2003 | "authors": [ 2004 | { 2005 | "name": "Sebastian Bergmann", 2006 | "email": "sebastian@phpunit.de" 2007 | } 2008 | ], 2009 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 2010 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 2011 | "support": { 2012 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 2013 | "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", 2014 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" 2015 | }, 2016 | "funding": [ 2017 | { 2018 | "url": "https://github.com/sebastianbergmann", 2019 | "type": "github" 2020 | } 2021 | ], 2022 | "time": "2024-07-03T05:00:13+00:00" 2023 | }, 2024 | { 2025 | "name": "sebastian/object-reflector", 2026 | "version": "4.0.1", 2027 | "source": { 2028 | "type": "git", 2029 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 2030 | "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" 2031 | }, 2032 | "dist": { 2033 | "type": "zip", 2034 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", 2035 | "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", 2036 | "shasum": "" 2037 | }, 2038 | "require": { 2039 | "php": ">=8.2" 2040 | }, 2041 | "require-dev": { 2042 | "phpunit/phpunit": "^11.0" 2043 | }, 2044 | "type": "library", 2045 | "extra": { 2046 | "branch-alias": { 2047 | "dev-main": "4.0-dev" 2048 | } 2049 | }, 2050 | "autoload": { 2051 | "classmap": [ 2052 | "src/" 2053 | ] 2054 | }, 2055 | "notification-url": "https://packagist.org/downloads/", 2056 | "license": [ 2057 | "BSD-3-Clause" 2058 | ], 2059 | "authors": [ 2060 | { 2061 | "name": "Sebastian Bergmann", 2062 | "email": "sebastian@phpunit.de" 2063 | } 2064 | ], 2065 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 2066 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 2067 | "support": { 2068 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 2069 | "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", 2070 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" 2071 | }, 2072 | "funding": [ 2073 | { 2074 | "url": "https://github.com/sebastianbergmann", 2075 | "type": "github" 2076 | } 2077 | ], 2078 | "time": "2024-07-03T05:01:32+00:00" 2079 | }, 2080 | { 2081 | "name": "sebastian/recursion-context", 2082 | "version": "6.0.2", 2083 | "source": { 2084 | "type": "git", 2085 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 2086 | "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" 2087 | }, 2088 | "dist": { 2089 | "type": "zip", 2090 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", 2091 | "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", 2092 | "shasum": "" 2093 | }, 2094 | "require": { 2095 | "php": ">=8.2" 2096 | }, 2097 | "require-dev": { 2098 | "phpunit/phpunit": "^11.0" 2099 | }, 2100 | "type": "library", 2101 | "extra": { 2102 | "branch-alias": { 2103 | "dev-main": "6.0-dev" 2104 | } 2105 | }, 2106 | "autoload": { 2107 | "classmap": [ 2108 | "src/" 2109 | ] 2110 | }, 2111 | "notification-url": "https://packagist.org/downloads/", 2112 | "license": [ 2113 | "BSD-3-Clause" 2114 | ], 2115 | "authors": [ 2116 | { 2117 | "name": "Sebastian Bergmann", 2118 | "email": "sebastian@phpunit.de" 2119 | }, 2120 | { 2121 | "name": "Jeff Welch", 2122 | "email": "whatthejeff@gmail.com" 2123 | }, 2124 | { 2125 | "name": "Adam Harvey", 2126 | "email": "aharvey@php.net" 2127 | } 2128 | ], 2129 | "description": "Provides functionality to recursively process PHP variables", 2130 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 2131 | "support": { 2132 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 2133 | "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", 2134 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" 2135 | }, 2136 | "funding": [ 2137 | { 2138 | "url": "https://github.com/sebastianbergmann", 2139 | "type": "github" 2140 | } 2141 | ], 2142 | "time": "2024-07-03T05:10:34+00:00" 2143 | }, 2144 | { 2145 | "name": "sebastian/type", 2146 | "version": "5.1.0", 2147 | "source": { 2148 | "type": "git", 2149 | "url": "https://github.com/sebastianbergmann/type.git", 2150 | "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" 2151 | }, 2152 | "dist": { 2153 | "type": "zip", 2154 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", 2155 | "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", 2156 | "shasum": "" 2157 | }, 2158 | "require": { 2159 | "php": ">=8.2" 2160 | }, 2161 | "require-dev": { 2162 | "phpunit/phpunit": "^11.3" 2163 | }, 2164 | "type": "library", 2165 | "extra": { 2166 | "branch-alias": { 2167 | "dev-main": "5.1-dev" 2168 | } 2169 | }, 2170 | "autoload": { 2171 | "classmap": [ 2172 | "src/" 2173 | ] 2174 | }, 2175 | "notification-url": "https://packagist.org/downloads/", 2176 | "license": [ 2177 | "BSD-3-Clause" 2178 | ], 2179 | "authors": [ 2180 | { 2181 | "name": "Sebastian Bergmann", 2182 | "email": "sebastian@phpunit.de", 2183 | "role": "lead" 2184 | } 2185 | ], 2186 | "description": "Collection of value objects that represent the types of the PHP type system", 2187 | "homepage": "https://github.com/sebastianbergmann/type", 2188 | "support": { 2189 | "issues": "https://github.com/sebastianbergmann/type/issues", 2190 | "security": "https://github.com/sebastianbergmann/type/security/policy", 2191 | "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" 2192 | }, 2193 | "funding": [ 2194 | { 2195 | "url": "https://github.com/sebastianbergmann", 2196 | "type": "github" 2197 | } 2198 | ], 2199 | "time": "2024-09-17T13:12:04+00:00" 2200 | }, 2201 | { 2202 | "name": "sebastian/version", 2203 | "version": "5.0.2", 2204 | "source": { 2205 | "type": "git", 2206 | "url": "https://github.com/sebastianbergmann/version.git", 2207 | "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" 2208 | }, 2209 | "dist": { 2210 | "type": "zip", 2211 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", 2212 | "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", 2213 | "shasum": "" 2214 | }, 2215 | "require": { 2216 | "php": ">=8.2" 2217 | }, 2218 | "type": "library", 2219 | "extra": { 2220 | "branch-alias": { 2221 | "dev-main": "5.0-dev" 2222 | } 2223 | }, 2224 | "autoload": { 2225 | "classmap": [ 2226 | "src/" 2227 | ] 2228 | }, 2229 | "notification-url": "https://packagist.org/downloads/", 2230 | "license": [ 2231 | "BSD-3-Clause" 2232 | ], 2233 | "authors": [ 2234 | { 2235 | "name": "Sebastian Bergmann", 2236 | "email": "sebastian@phpunit.de", 2237 | "role": "lead" 2238 | } 2239 | ], 2240 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 2241 | "homepage": "https://github.com/sebastianbergmann/version", 2242 | "support": { 2243 | "issues": "https://github.com/sebastianbergmann/version/issues", 2244 | "security": "https://github.com/sebastianbergmann/version/security/policy", 2245 | "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" 2246 | }, 2247 | "funding": [ 2248 | { 2249 | "url": "https://github.com/sebastianbergmann", 2250 | "type": "github" 2251 | } 2252 | ], 2253 | "time": "2024-10-09T05:16:32+00:00" 2254 | }, 2255 | { 2256 | "name": "staabm/side-effects-detector", 2257 | "version": "1.0.5", 2258 | "source": { 2259 | "type": "git", 2260 | "url": "https://github.com/staabm/side-effects-detector.git", 2261 | "reference": "d8334211a140ce329c13726d4a715adbddd0a163" 2262 | }, 2263 | "dist": { 2264 | "type": "zip", 2265 | "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", 2266 | "reference": "d8334211a140ce329c13726d4a715adbddd0a163", 2267 | "shasum": "" 2268 | }, 2269 | "require": { 2270 | "ext-tokenizer": "*", 2271 | "php": "^7.4 || ^8.0" 2272 | }, 2273 | "require-dev": { 2274 | "phpstan/extension-installer": "^1.4.3", 2275 | "phpstan/phpstan": "^1.12.6", 2276 | "phpunit/phpunit": "^9.6.21", 2277 | "symfony/var-dumper": "^5.4.43", 2278 | "tomasvotruba/type-coverage": "1.0.0", 2279 | "tomasvotruba/unused-public": "1.0.0" 2280 | }, 2281 | "type": "library", 2282 | "autoload": { 2283 | "classmap": [ 2284 | "lib/" 2285 | ] 2286 | }, 2287 | "notification-url": "https://packagist.org/downloads/", 2288 | "license": [ 2289 | "MIT" 2290 | ], 2291 | "description": "A static analysis tool to detect side effects in PHP code", 2292 | "keywords": [ 2293 | "static analysis" 2294 | ], 2295 | "support": { 2296 | "issues": "https://github.com/staabm/side-effects-detector/issues", 2297 | "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" 2298 | }, 2299 | "funding": [ 2300 | { 2301 | "url": "https://github.com/staabm", 2302 | "type": "github" 2303 | } 2304 | ], 2305 | "time": "2024-10-20T05:08:20+00:00" 2306 | }, 2307 | { 2308 | "name": "theseer/tokenizer", 2309 | "version": "1.2.3", 2310 | "source": { 2311 | "type": "git", 2312 | "url": "https://github.com/theseer/tokenizer.git", 2313 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 2314 | }, 2315 | "dist": { 2316 | "type": "zip", 2317 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 2318 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 2319 | "shasum": "" 2320 | }, 2321 | "require": { 2322 | "ext-dom": "*", 2323 | "ext-tokenizer": "*", 2324 | "ext-xmlwriter": "*", 2325 | "php": "^7.2 || ^8.0" 2326 | }, 2327 | "type": "library", 2328 | "autoload": { 2329 | "classmap": [ 2330 | "src/" 2331 | ] 2332 | }, 2333 | "notification-url": "https://packagist.org/downloads/", 2334 | "license": [ 2335 | "BSD-3-Clause" 2336 | ], 2337 | "authors": [ 2338 | { 2339 | "name": "Arne Blankerts", 2340 | "email": "arne@blankerts.de", 2341 | "role": "Developer" 2342 | } 2343 | ], 2344 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 2345 | "support": { 2346 | "issues": "https://github.com/theseer/tokenizer/issues", 2347 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 2348 | }, 2349 | "funding": [ 2350 | { 2351 | "url": "https://github.com/theseer", 2352 | "type": "github" 2353 | } 2354 | ], 2355 | "time": "2024-03-03T12:36:25+00:00" 2356 | } 2357 | ], 2358 | "aliases": [], 2359 | "minimum-stability": "stable", 2360 | "stability-flags": [], 2361 | "prefer-stable": false, 2362 | "prefer-lowest": false, 2363 | "platform": { 2364 | "php": ">=8.0", 2365 | "ext-simplexml": "*" 2366 | }, 2367 | "platform-dev": [], 2368 | "plugin-api-version": "2.6.0" 2369 | } 2370 | -------------------------------------------------------------------------------- /exemplu.php: -------------------------------------------------------------------------------- 1 | getMessage(); 9 | } 10 | }; 11 | $anaf = new ANAFAPIClient(ANAF_OAUTH,false, $Logger); 12 | var_dump($anaf->GetEntity("RO12345678")); -------------------------------------------------------------------------------- /gen-docs.bat: -------------------------------------------------------------------------------- 1 | docker run --rm -v %cd%/src:/data phpdoc/phpdoc:3 2 | -------------------------------------------------------------------------------- /gen-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #UNTESTED! 3 | docker run --rm -v ${PWD}/src:/data phpdoc/phpdoc:3 4 | -------------------------------------------------------------------------------- /run-tests-example.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set TEST_ANAF_REQUESTS=true 3 | set TEST_ANAF_CLIENT_CIF=YOUR_CIF_HERE 4 | REM Uncomment the following line to test only the empty list 5 | REM set ANAF_EMPTY_LIST_ONLY=true 6 | vendor\bin\phpunit tests --display-skipped -------------------------------------------------------------------------------- /run-tests-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ( 4 | export TEST_ANAF_REQUESTS=true 5 | export TEST_ANAF_CLIENT_CIF=YOUR_CIF_HERE 6 | # Uncomment the following line to test only the empty list 7 | # export ANAF_EMPTY_LIST_ONLY=true 8 | ./vendor/bin/phpunit tests --display-skipped 9 | ) -------------------------------------------------------------------------------- /src/ANAFAPIClient.php: -------------------------------------------------------------------------------- 1 | 'application/json', 73 | 'Accept' => 'application/json', 74 | ]; 75 | $this->Production = $production; 76 | $this->ErrorCallback = $errorCallback; 77 | $this->OAuthConfig = $OAuthConfig; 78 | if ($tokenFilePath == null) { 79 | $this->TokenFilePath = dirname(__FILE__) . "/ANAFAccessToken.json"; 80 | } else { 81 | $this->TokenFilePath = $tokenFilePath; 82 | } 83 | $this->PublicAPIClient = new Client($config); 84 | $config = []; 85 | $config['base_uri'] = 'https://api.anaf.ro'; 86 | $config['headers'] = [ 87 | 'Content-Type' => 'application/json', 88 | 'Accept' => 'application/json', 89 | ]; 90 | $this->AuthenticatedAPIClient = new Client($config); 91 | } 92 | 93 | /** 94 | * Check if a company is registered for TVA 95 | * @param string $cui 96 | * @return TVAResponse 97 | */ 98 | public function CheckTVAStatus(string $cui): TVAResponse 99 | { 100 | $cui = trim($cui); 101 | /** 102 | * @var TVAResponse $response 103 | * @noinspection PhpRedundantVariableDocTypeInspection 104 | */ 105 | $response = new TVAResponse(); 106 | $sanitizedCUI = $this->SanitizeCUI($cui); 107 | /** @noinspection PhpIncompatibleReturnTypeInspection */ 108 | return $this->DoEntityFetch($sanitizedCUI, $response); 109 | } 110 | 111 | /** 112 | * Get company/institution data from ANAF 113 | * @param string $cui 114 | * @return EntityResponse 115 | */ 116 | public function GetEntity(string $cui): EntityResponse 117 | { 118 | $cui = trim($cui); 119 | $sanitizedCUI = $this->SanitizeCUI($cui); 120 | $response = new EntityResponse(); 121 | 122 | return $this->DoEntityFetch($sanitizedCUI, $response); 123 | } 124 | 125 | /** 126 | * Sanitize CUI input for most requests. 127 | * Requests to the API should not contain the "RO" prefix and should be numeric. 128 | * This method is case-insensitive. 129 | * @param string $cui 130 | * @return string|false Will return false if the input contains non-numeric other than the "RO" prefix 131 | */ 132 | private function SanitizeCUI(string $cui): string|false 133 | { 134 | $cui = strtolower($cui); 135 | 136 | if (is_numeric($cui)) { 137 | return $cui; 138 | } 139 | 140 | if (str_starts_with($cui, "ro")) { 141 | $cui = substr($cui, 2); 142 | return is_numeric($cui) ? $cui : false; 143 | } 144 | 145 | return false; 146 | } 147 | 148 | /** 149 | * Helper method for the initial OAuth2 login 150 | * @return string 151 | */ 152 | public function GetLoginURL(): string 153 | { 154 | $provider = $this->GetOAuthProvider(); 155 | return $provider->getAuthorizationUrl(['token_content_type' => 'jwt']); 156 | } 157 | 158 | /** 159 | * Gets the access token from ANAF, should be called from the OAuth callback script 160 | * @param string $authCode 161 | * @return ?AccessToken 162 | * @throws IdentityProviderException 163 | */ 164 | public function ProcessOAuthCallback(string $authCode): ?AccessToken 165 | { 166 | $provider = $this->GetOAuthProvider(); 167 | $token = $provider->getAccessToken('authorization_code', ["code" => $authCode, 'token_content_type' => 'jwt']); 168 | try { 169 | $this->SaveAccessToken($token); 170 | return $token; 171 | } catch (Throwable $ex) { 172 | $this->CallErrorCallback("ANAF token save failed!", $ex); 173 | return null; 174 | } 175 | } 176 | 177 | /** 178 | * Gets the current access token (if it exists), tries to load it from the file if necessary 179 | * @return ?AccessToken 180 | */ 181 | public function GetAccessToken(): ?AccessToken 182 | { 183 | if ($this->AccessToken == null) { 184 | $this->LoadAccessToken(); 185 | } 186 | return $this->AccessToken; 187 | } 188 | 189 | /** 190 | * Saves access token to TokenFilePath 191 | * @param AccessToken $token 192 | * @return void 193 | * @see ANAFAPIClient::$TokenFilePath 194 | */ 195 | private function SaveAccessToken(AccessToken $token): void 196 | { 197 | if ($this->LockToken) { 198 | return; 199 | } 200 | $tokenJson = json_encode($token); 201 | file_put_contents($this->TokenFilePath, $tokenJson); 202 | $this->AccessToken = $token; 203 | } 204 | 205 | /** 206 | * Loads the access token from the file specified in TokenFilePath 207 | * @param bool $autoRefresh if true, the token will be refreshed if it has expired 208 | * @return AccessToken|null 209 | * @see ANAFAPIClient::$TokenFilePath 210 | * Note: This will be called automatically by @see ANAFAPIClient::SendANAFRequest() if the token is not already loaded and the request requires it. 211 | */ 212 | public function LoadAccessToken(bool $autoRefresh = true): ?AccessToken 213 | { 214 | if (file_exists($this->TokenFilePath) === false) { 215 | return null; 216 | } 217 | 218 | try { 219 | $tokenJson = file_get_contents($this->TokenFilePath); 220 | $token = new AccessToken(json_decode($tokenJson, true)); 221 | $this->AccessToken = $token; 222 | 223 | if ($autoRefresh && $this->TokenWillExpireSoon()) { 224 | if (!$this->RefreshAccessToken($token)) { 225 | $this->CallErrorCallback("ANAF token auto-refresh failed! Will not use expired token!"); 226 | $this->AccessToken = null; 227 | } 228 | } 229 | return $this->AccessToken; 230 | } catch (Throwable $ex) { 231 | $this->CallErrorCallback("ANAF token load failed!", $ex); 232 | return null; 233 | } 234 | } 235 | 236 | /** 237 | * Checks if the current access token will expire soon 238 | * @param int $soon Time in seconds to consider "soon" Default value is 24 hours 239 | * @return bool|null Null if the token is not set or does not have an expiration date 240 | */ 241 | public function TokenWillExpireSoon(int $soon = (3600 * 24)): bool|null 242 | { 243 | if ($this->AccessToken == null) { 244 | return null; 245 | } 246 | $expires = $this->AccessToken->getExpires(); 247 | if ($expires == null) { 248 | return null; 249 | } 250 | return $expires < time() + $soon; 251 | } 252 | 253 | /** 254 | * Refresh the access token using the refresh token 255 | * @param AccessToken|null $token if null the current access token @see ANAFAPIClient::$AccessToken will be used. 256 | * @return bool 257 | */ 258 | public function RefreshAccessToken(?AccessToken $token = null): bool 259 | { 260 | if ($this->LockToken) { 261 | return true; 262 | } 263 | $currentToken = $token ?? $this->LoadAccessToken(false); 264 | 265 | if ($currentToken == null) { 266 | $this->CallErrorCallback("ANAF token refresh failed!"); 267 | return false; 268 | } 269 | 270 | $provider = $this->GetOAuthProvider(); 271 | 272 | try { 273 | $newToken = $provider->getAccessToken('refresh_token', ["refresh_token" => $currentToken->getRefreshToken()]); 274 | } catch (Throwable $ex) { 275 | $this->CallErrorCallback("ANAF token refresh failed!", $ex); 276 | return false; 277 | } 278 | 279 | if ($newToken == null || empty($newToken->getToken())) { 280 | return false; 281 | } 282 | 283 | $this->SaveAccessToken($newToken); 284 | return true; 285 | } 286 | 287 | /** 288 | * Creates a new instance of the OAuth2 provider based on the configuration specified in the constructor 289 | * @return GenericProvider 290 | */ 291 | private function GetOAuthProvider(): GenericProvider 292 | { 293 | return new GenericProvider($this->OAuthConfig); 294 | } 295 | 296 | /** 297 | * All requests to ANAF are made through this method 298 | * @param string $Method The URL 299 | * @param string|null $body The request body (if this is different from null, the request will be a POST request) 300 | * @param array|null $queryParams Query parameters (if teh value is ["ex1"=>"val"] ?ex1=val will be appended to the URL) 301 | * @param bool $hasAuth Should be true of the request requires authentication 302 | * @param string $contentType Content type header for the outgoing request 303 | * @param float|null $timeoutOverride if set, will override the default timeout (ANAFAPIClient::$Timeout) 304 | * @return ResponseInterface 305 | * @throws GuzzleException 306 | */ 307 | private function SendANAFRequest(string $Method, ?string $body = null, array|null $queryParams = null, bool $hasAuth = false, string $contentType = "application/json", float|null $timeoutOverride = null): ResponseInterface 308 | { 309 | $client = $hasAuth ? $this->AuthenticatedAPIClient : $this->PublicAPIClient; 310 | $options = ["headers" => 311 | [ 312 | "Content-Type" => $contentType, 313 | "Accept" => "*/*", 314 | "Cache-Control" => "no-cache", 315 | ] 316 | ]; 317 | 318 | if ($hasAuth) { 319 | if (!$this->HasAccessToken()) { 320 | throw new Exception("ANAF API Error: No token"); 321 | } 322 | 323 | $options["headers"]["Authorization"] = "Bearer " . $this->AccessToken->getToken(); 324 | } 325 | $options["query"] = $queryParams; 326 | $options["timeout"] = $timeoutOverride === null ? $this->Timeout : $timeoutOverride; 327 | 328 | if ($body === null) { 329 | return $client->get($Method, $options); 330 | } 331 | 332 | $options['body'] = $body; 333 | return $client->post($Method, $options); 334 | } 335 | 336 | /** 337 | * Base method for fetching company/institution data from ANAF 338 | * @param false|string $sanitizedCUI 339 | * @param EntityResponse $response 340 | * @return EntityResponse 341 | */ 342 | public function DoEntityFetch(false|string $sanitizedCUI, EntityResponse $response): EntityResponse 343 | { 344 | try { 345 | if ($sanitizedCUI === false || !self::ValidateCIF($sanitizedCUI)) { 346 | $response->LastError = new ANAFException("CUI invalid", ANAFException::INVALID_INPUT); 347 | return $response; 348 | } 349 | 350 | $requestBody = [["cui" => $sanitizedCUI, "data" => date("Y-m-d")]]; 351 | $httpResponse = $this->SendANAFRequest("api/PlatitorTvaRest/v9/tva", json_encode($requestBody)); 352 | 353 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 354 | $content = $httpResponse->getBody()->getContents(); 355 | //var_dump($content); 356 | $response->rawResponse = $content; 357 | $response->Parse(); 358 | return $response; 359 | } 360 | $response->LastError = new ANAFException("HTTP Error: " . $httpResponse->getStatusCode(), ANAFException::HTTP_ERROR); 361 | } catch (Throwable $ex) { 362 | $response->LastError = $ex; 363 | $this->CallErrorCallback("ANAF API Error", $ex); 364 | } 365 | return $response; 366 | } 367 | 368 | /** 369 | * @param string $ubl UBL XML content to upload 370 | * @param string $sellerCIF CUI of the company which issued the invoice 371 | * @param bool $extern If true, the invoice is marked as "extern" (not issued to a romanian company) 372 | * @param bool $autoFactura If true, the invoice is marked as "autofactura" (issued by the buyer in the sellers name) 373 | * @return UBLUploadResponse 374 | */ 375 | public function UploadEFactura(string $ubl, string $sellerCIF, bool $extern = false, bool $autoFactura = false): UBLUploadResponse 376 | { 377 | $response = new UBLUploadResponse(); 378 | 379 | try { 380 | $modeName = $this->Production ? "prod" : "test"; 381 | $method = "/$modeName/FCTEL/rest/upload"; 382 | $queryParams = ["standard" => "UBL", "cif" => $sellerCIF]; 383 | 384 | if ($extern) { 385 | $queryParams["extern"] = "DA"; 386 | } 387 | 388 | if ($autoFactura) { 389 | $queryParams["autofactura"] = "DA"; 390 | } 391 | 392 | $httpResponse = $this->SendANAFRequest($method, $ubl, $queryParams, true); 393 | 394 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 395 | //var_dump($httpResponse); 396 | $content = $httpResponse->getBody()->getContents(); 397 | $response->rawResponse = $content; 398 | $response->Parse(); 399 | 400 | } 401 | } catch (Throwable $ex) { 402 | $response->LastError = $ex; 403 | $this->CallErrorCallback("ANAF API Error", $ex); 404 | } 405 | 406 | return $response; 407 | } 408 | 409 | /** 410 | * Download Answer from ANAF 411 | * To obtain the ID, use ListAnswers 412 | * @param string $id 413 | * @return string either an error message (starts with "ERROR_") or zip file content 414 | * @see ANAFAPIClient::ListAnswers() 415 | */ 416 | public function DownloadAnswer(string $id): string 417 | { 418 | $modeName = $this->Production ? "prod" : "test"; 419 | $method = "/$modeName/FCTEL/rest/descarcare?id=$id"; 420 | 421 | try { 422 | $httpResponse = $this->SendANAFRequest($method, null, null, true); 423 | 424 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 425 | //var_dump($httpResponse); 426 | $content = $httpResponse->getBody()->getContents(); 427 | 428 | if (str_starts_with($content, "PK")) { 429 | return $content; 430 | } 431 | return "ERROR_BAD_CONTENT"; 432 | 433 | } 434 | } catch (Throwable $ex) { 435 | $this->CallErrorCallback("ANAF API Error", $ex); 436 | return "ERROR"; 437 | } 438 | 439 | return "ERROR_NO_RESPONSE"; 440 | } 441 | 442 | /** 443 | * Convert UBL XML to PDF using the ANAF API 444 | * @param string $ubl 445 | * @param string $metadata 446 | * @param bool $authenticated if true the request will use the OAuth2 API endpoint 447 | * @param float|null $timeoutOverride if set, will override the default timeout (ANAFAPIClient::$Timeout) 448 | * @return string|false 449 | */ 450 | public function UBL2PDF(string $ubl, string $metadata, bool $authenticated = true, float|null $timeoutOverride = null): string|false 451 | { 452 | if (str_contains($ubl, 'xsi:schemaLocation')) { 453 | $ubl = self::RemoveSchemaLocationAttribute($ubl); 454 | } 455 | $method = "/prod/FCTEL/rest/transformare/FACT1/DA"; 456 | try { 457 | $timeoutOverride = $timeoutOverride ?? $this->Timeout; 458 | $httpResponse = $this->SendANAFRequest($method, $ubl, null, $authenticated, "text/plain", $timeoutOverride); 459 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 460 | $content = $httpResponse->getBody()->getContents(); 461 | if (str_starts_with($content, "%PDF")) { 462 | return $content; 463 | } 464 | $this->CallErrorCallback("Bad content format, expected PDF (UBL2PDF) - $metadata"); 465 | $this->CallErrorCallback($content); 466 | return false; 467 | 468 | } 469 | } catch (Throwable $ex) { 470 | $this->CallErrorCallback("ANAF API Error", $ex); 471 | return false; 472 | } 473 | 474 | return false; 475 | } 476 | 477 | /** 478 | * Removes the xsi:schemaLocation attribute from the root element of the UBL XML 479 | * @param $xmlString 480 | * @return string 481 | */ 482 | public static function RemoveSchemaLocationAttribute($xmlString): string 483 | { 484 | $pattern = '/(xsi:schemaLocation\s*=\s*["\'][^"\']*["\'])/i'; 485 | $xmlString = preg_replace($pattern, '', $xmlString, -1); 486 | return preg_replace('/\s{2,}/', ' ', $xmlString); 487 | } 488 | 489 | /** 490 | * Get answer list for a company (authenticated user must have access to the company!) 491 | * @param int $cif 492 | * @param int $days Number of days to look back 493 | * @param string|null $filter 494 | * @param bool $usePaginationIfNeeded If true and the answer has LastError code ANAFException::MESSAGE_LIST_TOO_LONG, the method will try to fetch the answers using pagination 495 | * @param float|int $endDateOffsetForPagination ANAF API gives an error if the end date is "in the future" based on their internal clock, so end date will be currentTime-$endDateOffsetForPagination. Default is 5 minutes 496 | * @return ANAFAnswerListResponse 497 | * @see ANAFAPIClient::ListAnswersWithPagination() 498 | */ 499 | public function ListAnswers(int $cif, int $days = 60, string|null $filter = null, bool $usePaginationIfNeeded = false, $endDateOffsetForPagination = 5 * 60): ANAFAnswerListResponse 500 | { 501 | if ($filter != null) { 502 | $filter = strtoupper($filter); 503 | if (!$this->ValidateFilter($filter)) { 504 | return ANAFAnswerListResponse::CreateError(new ANAFException("Invalid filter", ANAFException::INVALID_INPUT)); 505 | } 506 | } 507 | $modeName = $this->Production ? "prod" : "test"; 508 | $method = "/$modeName/FCTEL/rest/listaMesajeFactura?zile=$days&cif=$cif"; 509 | if ($filter != null) { 510 | $method .= "&filtru=$filter"; 511 | } 512 | try { 513 | $httpResponse = $this->SendANAFRequest($method, null, null, true); 514 | 515 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 516 | //var_dump($httpResponse); 517 | $content = $httpResponse->getBody()->getContents(); 518 | $answer = ANAFAnswerListResponse::Create($content); 519 | if ($answer->IsSuccess() || !$usePaginationIfNeeded) { 520 | return $answer; 521 | } 522 | if ($answer->LastError->getCode() == ANAFException::MESSAGE_LIST_TOO_LONG) { 523 | $startDate = time() - ($days * 24 * 3600); 524 | $endDate = time() - $endDateOffsetForPagination; 525 | return $this->ListAnswersWithPagination($startDate, $endDate, $cif, null, $filter); 526 | } 527 | } 528 | } catch (Throwable $ex) { 529 | $this->CallErrorCallback("ANAF API Error", $ex); 530 | return ANAFAnswerListResponse::CreateError($ex); 531 | } 532 | 533 | return ANAFAnswerListResponse::CreateError(new ANAFException("No response or error", ANAFException::UNKNOWN_ERROR)); 534 | } 535 | 536 | /** 537 | * Get answer list for a company (authenticated user must have access to the company!) 538 | * Uses the paged API end point 539 | * Answers will be returned in a unified list 540 | * @param int $startTime 541 | * @param int $endTime 542 | * @param int $cif 543 | * @param int|null $specificPage 544 | * @param string|null $filter 545 | * @return PagedAnswerListResponse 546 | */ 547 | public function ListAnswersWithPagination(int $startTime, int $endTime, int $cif, int $specificPage = null, string|null $filter = null): PagedAnswerListResponse 548 | { 549 | if ($filter != null) { 550 | $filter = strtoupper($filter); 551 | if (!$this->ValidateFilter($filter)) { 552 | return PagedAnswerListResponse::CreateError(new ANAFException("Invalid filter", ANAFException::INVALID_INPUT)); 553 | } 554 | } 555 | if ($specificPage != null) { 556 | $response = $this->GetAnswerPage($startTime, $endTime, $cif, $specificPage, $filter); 557 | if ($response->IsSuccess()) { 558 | return new PagedAnswerListResponse([$response], []); 559 | } 560 | return PagedAnswerListResponse::CreateError($response->LastError); 561 | } 562 | /** 563 | * @var InternalPagedAnswersResponse[] $pages 564 | */ 565 | $pages = []; 566 | $errors = []; 567 | $currentPage = 1; 568 | $canFetchNextPage = true; 569 | while ($canFetchNextPage) { 570 | $response = $this->GetAnswerPage($startTime, $endTime, $cif, $currentPage, $filter); 571 | if ($response->IsSuccess()) { 572 | if (count($response->mesaje) > 0) { 573 | $pages[] = $response; 574 | } 575 | } else { 576 | $errors[] = $response; 577 | } 578 | $canFetchNextPage = !$response->IsLastPage(); 579 | $currentPage++; 580 | } 581 | 582 | return new PagedAnswerListResponse($pages, $errors); 583 | } 584 | 585 | /** 586 | * Call the paged answer list endpoint with a speicifc page number 587 | * @param int $startTime 588 | * @param int $endTime 589 | * @param int $cif 590 | * @param int $pageNumber 591 | * @param string|null $filter 592 | * @return InternalPagedAnswersResponse 593 | */ 594 | public function GetAnswerPage(int $startTime, int $endTime, int $cif, int $pageNumber, string|null $filter = null): InternalPagedAnswersResponse 595 | { 596 | $modeName = $this->Production ? "prod" : "test"; 597 | $actualStart = $startTime * 1000; 598 | $actualEnd = $endTime * 1000; 599 | $method = "/$modeName/FCTEL/rest/listaMesajePaginatieFactura?startTime=$actualStart&endTime=$actualEnd&cif=$cif&pagina=$pageNumber"; 600 | if ($filter != null) { 601 | $method .= "&filtru=$filter"; 602 | } 603 | try { 604 | $httpResponse = $this->SendANAFRequest($method, null, null, true); 605 | 606 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 607 | //var_dump($httpResponse); 608 | $content = $httpResponse->getBody()->getContents(); 609 | return InternalPagedAnswersResponse::Create($content); 610 | } 611 | } catch (Throwable $ex) { 612 | $this->CallErrorCallback("ANAF API Error", $ex); 613 | return InternalPagedAnswersResponse::CreateError($ex); 614 | } 615 | 616 | return InternalPagedAnswersResponse::CreateError(new ANAFException("No response or error", ANAFException::UNKNOWN_ERROR)); 617 | 618 | } 619 | 620 | /** 621 | * ATTENTION DOES NOT FUNCTION RELIABLY. 622 | * The API randomly returns "nok" for valid invoices. 623 | * @param string $ubl 624 | * @param bool $authenticated If true, the request will use the OAuth2 API endpoint 625 | * @return ANAFVerifyResponse 626 | * @deprecated Use at your own risk. Read the comment above. 627 | */ 628 | public function VerifyXML(string $ubl, bool $authenticated = true): ANAFVerifyResponse 629 | { 630 | try { 631 | $method = "/prod/FCTEL/rest/validare/FACT1"; 632 | $httpResponse = $this->SendANAFRequest($method, $ubl, null, $authenticated, "text/plain"); 633 | if ($httpResponse->getStatusCode() >= 200 && $httpResponse->getStatusCode() < 300) { 634 | //var_dump($httpResponse); 635 | $contentString = $httpResponse->getBody()->getContents(); 636 | return ANAFVerifyResponse::Create($contentString); 637 | } 638 | } catch (Throwable $ex) { 639 | return ANAFVerifyResponse::CreateError($ex); 640 | } 641 | 642 | $this->CallErrorCallback("ANAF VERIFY ERROR: NO RESPONSE OR ERROR"); 643 | return ANAFVerifyResponse::CreateError(new ANAFException("No response or error", ANAFException::UNKNOWN_ERROR)); 644 | } 645 | 646 | /** 647 | * Currently accepted filter chars are: E, T, P, R 648 | * @param string $filter 649 | * @return bool 650 | */ 651 | private function ValidateFilter(string $filter): bool 652 | { 653 | if (strlen($filter) != 1) { 654 | return false; 655 | } 656 | $acceptedFilters = ["E", "T", "P", "R"]; 657 | return in_array($filter, $acceptedFilters); 658 | } 659 | 660 | /** 661 | * Calls ErrorCallback with given params. 662 | * @param string $message 663 | * @param Throwable|null $ex 664 | * @return void 665 | * @see ANAFAPIClient::$ErrorCallback 666 | */ 667 | private function CallErrorCallback(string $message, ?Throwable $ex = null): void 668 | { 669 | if ($this->ErrorCallback != null) { 670 | ($this->ErrorCallback)($message, $ex); 671 | return; 672 | } 673 | 674 | error_log($message); 675 | 676 | if ($ex != null) { 677 | error_log($ex->getMessage()); 678 | error_log($ex->getTraceAsString()); 679 | } 680 | } 681 | 682 | /** 683 | * Check if the client has a valid access token. 684 | */ 685 | public function HasAccessToken(): bool 686 | { 687 | return $this->GetAccessToken() != null; 688 | } 689 | 690 | /** 691 | * @return bool 692 | * @see ANAFAPIClient::$Production 693 | */ 694 | public function IsProduction(): bool 695 | { 696 | return $this->Production; 697 | } 698 | 699 | /** 700 | * Verifies a CUI given the official checksum algorithm 701 | * @param string|false $cif 702 | * @return bool 703 | */ 704 | public static function ValidateCIF(string|false $cif): bool 705 | { 706 | if ($cif === false) { 707 | return false; 708 | } 709 | /** @noinspection PhpConditionAlreadyCheckedInspection */ 710 | if (!is_int($cif)) { 711 | $cif = strtoupper($cif); 712 | if (str_starts_with($cif, 'RO')) { 713 | $cif = substr($cif, 2); 714 | } 715 | $cif = (int)trim($cif); 716 | } 717 | 718 | if (strlen($cif) > 10 || strlen($cif) < 2) { 719 | return false; 720 | } 721 | $v = 753217532; 722 | $c1 = $cif % 10; 723 | $cif = (int)($cif / 10); 724 | $t = 0; 725 | while ($cif > 0) { 726 | $t += ($cif % 10) * ($v % 10); 727 | $cif = (int)($cif / 10); 728 | $v = (int)($v / 10); 729 | } 730 | $c2 = $t * 10 % 11; 731 | if ($c2 == 10) { 732 | $c2 = 0; 733 | } 734 | return $c1 === $c2; 735 | } 736 | } 737 | -------------------------------------------------------------------------------- /src/ANAFEntity/Address.php: -------------------------------------------------------------------------------- 1 | $property = $parsed[$prefix . $property]; 44 | } 45 | } 46 | return $result; 47 | } catch (Throwable) { 48 | return null; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/ANAFEntity/Entity.php: -------------------------------------------------------------------------------- 1 | date_generale = isset($parsedData->date_generale) ? GeneralInfo::CreateFromParsed($parsedData->date_generale) : null; 50 | $entity->inregistrare_scop_Tva = isset($parsedData->inregistrare_scop_Tva) ? TVAInfo::CreateFromParsed($parsedData->inregistrare_scop_Tva) : null; 51 | $entity->inregistrare_RTVAI = isset($parsedData->inregistrare_RTVAI) ? RTVAInfo::CreateFromParsed($parsedData->inregistrare_RTVAI) : null; 52 | $entity->stare_inactiv = isset($parsedData->stare_inactiv) ? InactiveInfo::CreateFromParsed($parsedData->stare_inactiv) : null; 53 | $entity->inregistrare_SplitTVA = isset($parsedData->inregistrare_SplitTVA) ? SplitTVAInfo::CreateFromParsed($parsedData->inregistrare_SplitTVA) : null; 54 | $entity->adresa_sediu_social = isset($parsedData->adresa_sediu_social) ? Address::CreateFromParsed($parsedData->adresa_sediu_social) : null; 55 | $entity->adresa_domiciliu_fiscal = isset($parsedData->adresa_domiciliu_fiscal) ? Address::CreateFromParsed($parsedData->adresa_domiciliu_fiscal) : null; 56 | return $entity; 57 | } 58 | 59 | /** 60 | * Try to extract Bucharest's sector from the address 61 | * Will return null if the address is not set or if the address does not contain a sector. 62 | * (Looks for the word "sector" followed by a digit, case-insensitive, surrounded by spaces or punctuation) 63 | * @return int|null 64 | */ 65 | public function GetSector(): ?int 66 | { 67 | if ($this->date_generale == null) { 68 | return null; 69 | } 70 | 71 | $toSearch = $this->date_generale->adresa; 72 | if (empty($toSearch)) { 73 | return null; 74 | } 75 | 76 | $toSearch = strtolower($toSearch); 77 | 78 | if (preg_match('/\bsector\b\s*(\d)(?=\s|\p{P}|\z)/u', $toSearch, $matches)) { 79 | return (int)$matches[1]; // Cast the captured digit to an integer and return it 80 | } 81 | return null; 82 | } 83 | 84 | /** 85 | * Check if CIF is "radiat" (closed?) 86 | * @return bool 87 | */ 88 | public function IsRadiat(): bool 89 | { 90 | return str_starts_with(strtolower($this->date_generale->stare_inregistrare), "radi"); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/ANAFEntity/GeneralInfo.php: -------------------------------------------------------------------------------- 1 | $property = $parsed[$property]; 46 | } 47 | } 48 | return $result; 49 | } catch (Throwable $th) { 50 | error_log($th->getMessage()); 51 | error_log($th->getTraceAsString()); 52 | return null; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/ANAFEntity/InactiveInfo.php: -------------------------------------------------------------------------------- 1 | dataInactivare = $parsedData->dataInactivare ?? ""; 27 | $inactiveInfo->dataReactivare = $parsedData->dataReactivare ?? ""; 28 | $inactiveInfo->dataPublicare = $parsedData->dataPublicare ?? ""; 29 | $inactiveInfo->dataRadiere = $parsedData->dataRadiere ?? ""; 30 | $inactiveInfo->statusInactivi = $parsedData->statusInactivi ?? false; 31 | return $inactiveInfo; 32 | } 33 | } -------------------------------------------------------------------------------- /src/ANAFEntity/RTVAInfo.php: -------------------------------------------------------------------------------- 1 | dataInceputTvaInc = $parsedData->dataInceputTvaInc ?? ""; 27 | $rtvaInfo->dataSfarsitTvaInc = $parsedData->dataSfarsitTvaInc ?? ""; 28 | $rtvaInfo->dataActualizareTvaInc = $parsedData->dataActualizareTvaInc ?? ""; 29 | $rtvaInfo->dataPublicareTvaInc = $parsedData->dataPublicareTvaInc ?? ""; 30 | $rtvaInfo->tipActTvaInc = $parsedData->tipActTvaInc ?? ""; 31 | $rtvaInfo->statusTvaIncasare = $parsedData->statusTvaIncasare ?? false; 32 | return $rtvaInfo; 33 | } 34 | } -------------------------------------------------------------------------------- /src/ANAFEntity/SplitTVAInfo.php: -------------------------------------------------------------------------------- 1 | dataInceputSplitTVA = $parsedData->dataInceputSplitTVA ?? ""; 25 | $splitTVAInfo->dataAnulareSplitTVA = $parsedData->dataAnulareSplitTVA ?? ""; 26 | $splitTVAInfo->statusSplitTVA = $parsedData->statusSplitTVA ?? false; 27 | return $splitTVAInfo; 28 | } 29 | } -------------------------------------------------------------------------------- /src/ANAFEntity/TVAInfo.php: -------------------------------------------------------------------------------- 1 | scpTVA = $parsedData->scpTVA ?? false; 24 | $tvaInfo->perioade_TVA = $parsedData->perioade_TVA ?? []; 25 | return $tvaInfo; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Responses/ANAFAnswer.php: -------------------------------------------------------------------------------- 1 | data_creare = $parsed->data_creare; 21 | $response->cif = $parsed->cif; 22 | $response->id_solicitare = $parsed->id_solicitare; 23 | $response->detalii = $parsed->detalii; 24 | $response->tip = $parsed->tip; 25 | $response->id = $parsed->id; 26 | return $response; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Responses/ANAFAnswerListResponse.php: -------------------------------------------------------------------------------- 1 | eroare)) { 28 | $code = ANAFException::REMOTE_EXCEPTION; 29 | if (str_starts_with($parsed->eroare, "Lista de mesaje este mai mare decat numarul de")) { 30 | $code = ANAFException::MESSAGE_LIST_TOO_LONG; 31 | } 32 | $this->InternalCreateError($parsed->eroare, $code); 33 | return; 34 | } 35 | 36 | $this->serial = $parsed->serial; 37 | $this->cui = $parsed->cui; 38 | $this->titlu = $parsed->titlu; 39 | $this->mesaje = []; 40 | foreach ($parsed->mesaje as $mesaj) { 41 | $this->mesaje[] = ANAFAnswer::CreateFromParsed($mesaj); 42 | } 43 | } 44 | 45 | /** 46 | * Create an error response 47 | * For internal use! 48 | * @param Throwable $error 49 | * @return ANAFAnswerListResponse 50 | */ 51 | public static function CreateError(Throwable $error): ANAFAnswerListResponse 52 | { 53 | $result = new ANAFAnswerListResponse(); 54 | $result->LastError = $error; 55 | return $result; 56 | } 57 | 58 | public function Parse(): void 59 | { 60 | try { 61 | $parsed = $this->CommonParseJSON($this->rawResponse); 62 | //var_dump($parsed); 63 | if ($parsed == null && !$this->HasError()) { 64 | $this->InternalCreateError("Internal error parsing response"); 65 | return; 66 | } 67 | if (strtolower($parsed->titlu) == "lista mesaje" 68 | && isset($parsed->eroare) 69 | && !isset($parsed->mesaje) 70 | && !isset($parsed->serial) 71 | && !isset($parsed->cui) 72 | && str_contains(strtolower($parsed->eroare), "nu exista mesaje")) { 73 | unset($parsed->eroare); 74 | //var_dump($this->LastError); 75 | $parsed->mesaje = []; 76 | $parsed->serial = ""; 77 | $parsed->cui = ""; 78 | } 79 | 80 | $this->CopyFromParsed($parsed); 81 | } catch (Throwable $ex) { 82 | $this->LastError = $ex; 83 | } 84 | } 85 | 86 | public static function Create($rawResponse): self 87 | { 88 | $response = new ANAFAnswerListResponse(); 89 | $response->rawResponse = $rawResponse; 90 | $response->Parse(); 91 | return $response; 92 | } 93 | } -------------------------------------------------------------------------------- /src/Responses/ANAFException.php: -------------------------------------------------------------------------------- 1 | LastError != null; 24 | } 25 | 26 | public function IsSuccess(): bool 27 | { 28 | return !$this->HasError(); 29 | } 30 | 31 | /** 32 | * Utility method for implementers to create an error 33 | * @param string $message 34 | * @param int $code 35 | * @param Throwable|null $previous 36 | * @return void 37 | */ 38 | protected function InternalCreateError(string $message, int $code = ANAFException::UNKNOWN_ERROR, ?Throwable $previous = null): void 39 | { 40 | $this->LastError = new ANAFException($message, $code, $previous); 41 | } 42 | 43 | protected function CommonParseJSON(string|null $response): stdClass|array|null 44 | { 45 | if (empty($response)) { 46 | $this->InternalCreateError("No response to parse", ANAFException::EMPTY_RAW_RESPONSE); 47 | return null; 48 | } 49 | $parsed = json_decode($response); 50 | $parseError = json_last_error(); 51 | if ($parseError !== JSON_ERROR_NONE) { 52 | $this->InternalCreateError("JSON parse error:" . $parseError, ANAFException::JSON_PARSE_ERROR); 53 | return null; 54 | } 55 | return $parsed; 56 | } 57 | } -------------------------------------------------------------------------------- /src/Responses/ANAFVerifyResponse.php: -------------------------------------------------------------------------------- 1 | HasError()) { 23 | return false; 24 | } 25 | if (strtolower($this->stare) == "ok") { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | /** 32 | * Similar to 33 | * @param mixed $parsed 34 | * @return void 35 | */ 36 | private function CopyFromParsed(mixed $parsed): void 37 | { 38 | $this->stare = $parsed->stare; 39 | if (!isset($parsed->Messages) || $parsed->Messages == null) { 40 | $this->Messages = []; 41 | } else { 42 | $this->Messages = $parsed->Messages; 43 | } 44 | $this->trace_id = $parsed->trace_id; 45 | } 46 | 47 | 48 | public function Parse(): void 49 | { 50 | try { 51 | $parsed = $this->CommonParseJSON($this->rawResponse); 52 | if ($parsed == null && !$this->HasError()) { 53 | $this->InternalCreateError("Internal error parsing response"); 54 | } 55 | $this->CopyFromParsed($parsed); 56 | } catch (Throwable $ex) { 57 | $this->InternalCreateError($ex->getMessage(), ANAFException::JSON_UNKNOWN_ERROR, $this->LastError); 58 | } 59 | } 60 | 61 | public static function Create($rawResponse): ANAFVerifyResponse 62 | { 63 | $response = new ANAFVerifyResponse(); 64 | $response->rawResponse = $rawResponse; 65 | $response->Parse(); 66 | return $response; 67 | } 68 | 69 | public static function CreateError(Throwable $error): ANAFVerifyResponse 70 | { 71 | $response = new ANAFVerifyResponse(); 72 | $response->LastError = $error; 73 | return $response; 74 | } 75 | } -------------------------------------------------------------------------------- /src/Responses/EntityResponse.php: -------------------------------------------------------------------------------- 1 | CommonParseJSON($this->rawResponse); 19 | if ($parsed == null && !$this->HasError()) { 20 | $this->InternalCreateError("Internal error parsing response"); 21 | return; 22 | } 23 | } catch (Throwable $ex) { 24 | $this->InternalCreateError($ex->getMessage(), ANAFException::JSON_UNKNOWN_ERROR, $this->LastError); 25 | return; 26 | } 27 | if (!isset($parsed->found) || !is_countable($parsed->found)) { 28 | $this->InternalCreateError("Missing/invalid found array, bad response structure?", ANAFException::UNEXPECTED_RESPONSE_STRUCTURE); 29 | return; 30 | } 31 | if (sizeof($parsed->found) == 0) { 32 | $this->InternalCreateError("CUI invalid (nu s-a regasit in baza de date ANAF)!", ANAFException::RESULT_NOT_FOUND); 33 | return; 34 | } 35 | if (sizeof($parsed->found) != 1) { 36 | $this->LastError = new ANAFException("Too many results", ANAFException::TOO_MANY_RESULTS); 37 | return; 38 | } 39 | if (!isset($parsed->found[0]->date_generale) || !isset($parsed->found[0]->inregistrare_scop_Tva)) { 40 | $this->InternalCreateError("Missing/invalid date_generale or inregistrare_scop_Tva, bad response structure?", ANAFException::INCOMPLETE_RESPONSE); 41 | return; 42 | } 43 | if (!isset($parsed->found[0]->inregistrare_scop_Tva->scpTVA) || !is_bool($parsed->found[0]->inregistrare_scop_Tva->scpTVA)) { 44 | $this->InternalCreateError("Missing/invalid scpTVA, bad response structure?", ANAFException::INCOMPLETE_RESPONSE); 45 | return; 46 | } 47 | $this->Entity = Entity::CreateFromParsed($parsed->found[0]); 48 | } 49 | 50 | public static function CreateError(Throwable $error): EntityResponse 51 | { 52 | $result = new EntityResponse(); 53 | $result->LastError = $error; 54 | return $result; 55 | } 56 | } -------------------------------------------------------------------------------- /src/Responses/InternalPagedAnswersResponse.php: -------------------------------------------------------------------------------- 1 | CommonParseJSON($this->rawResponse); 25 | } catch (Throwable $th) { 26 | $this->LastError = $th; 27 | return; 28 | } 29 | if (str_starts_with(strtolower($parsed->titlu), "lista mesaje") 30 | && isset($parsed->eroare) 31 | && !isset($parsed->mesaje) 32 | && !isset($parsed->serial) 33 | && !isset($parsed->cui) 34 | && preg_match($pattern, $parsed->eroare)) { 35 | $parsed->mesaje = []; 36 | $parsed->serial = ""; 37 | $parsed->cui = ""; 38 | return; 39 | } 40 | parent::Parse(); 41 | if (isset($this->mesaje) && count($this->mesaje) > 0) { 42 | $this->numar_inregistrari_in_pagina = $parsed->numar_inregistrari_in_pagina; 43 | $this->numar_total_inregistrari_per_pagina = $parsed->numar_total_inregistrari_per_pagina; 44 | $this->numar_total_inregistrari = $parsed->numar_total_inregistrari; 45 | $this->numar_total_pagini = $parsed->numar_total_pagini; 46 | $this->index_pagina_curenta = $parsed->index_pagina_curenta; 47 | } 48 | } 49 | 50 | /** 51 | * Check if the current page is the last page 52 | * Will always return true if the response is not successful 53 | * @return bool 54 | */ 55 | public function IsLastPage(): bool 56 | { 57 | if ($this->IsSuccess()) { 58 | return $this->index_pagina_curenta >= $this->numar_total_pagini; 59 | } 60 | return true; 61 | } 62 | 63 | /** 64 | * Create an error response 65 | * For internal use! 66 | * @param Throwable $error 67 | * @return InternalPagedAnswersResponse 68 | */ 69 | public static function CreateError(Throwable $error): InternalPagedAnswersResponse 70 | { 71 | $result = new InternalPagedAnswersResponse(); 72 | $result->LastError = $error; 73 | return $result; 74 | } 75 | 76 | public static function Create($rawResponse): InternalPagedAnswersResponse 77 | { 78 | $result = new InternalPagedAnswersResponse(); 79 | $result->rawResponse = $rawResponse; 80 | $result->Parse(); 81 | return $result; 82 | } 83 | } -------------------------------------------------------------------------------- /src/Responses/PagedAnswerListResponse.php: -------------------------------------------------------------------------------- 1 | ErrorPages = $errors; 40 | $this->PageCount = count($responses); 41 | if ($this->PageCount == 0) { 42 | $this->Pages = []; 43 | $this->mesaje = []; 44 | return; 45 | } 46 | $this->Pages = $responses; 47 | $this->cui = $responses[0]->cui; 48 | $this->serial = $responses[0]->serial; 49 | $this->titlu = $responses[0]->titlu; 50 | $this->mesaje = []; 51 | if (count($errors) != 0) { 52 | if (count($errors) == 1) { 53 | $this->LastError = $errors[0]->LastError; 54 | } else { 55 | $this->LastError = new ANAFException("Multiple page requests result in an error", ANAFException::COMPOUND_ERROR); 56 | } 57 | } 58 | for ($i = 0; $i < $this->PageCount; $i++) { 59 | $this->mesaje = array_merge($this->mesaje, $responses[$i]->mesaje); 60 | } 61 | } 62 | 63 | public static function Create($rawResponse): PagedAnswerListResponse 64 | { 65 | throw new Exception("This class is not meant to be created from a raw response"); 66 | } 67 | 68 | public static function CreateError(Throwable $error): PagedAnswerListResponse 69 | { 70 | $response = new PagedAnswerListResponse([], []); 71 | $response->LastError = $error; 72 | return $response; 73 | 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/Responses/TVAResponse.php: -------------------------------------------------------------------------------- 1 | IsSuccess()) 15 | $this->IsTVARegistered = $this->Entity->inregistrare_scop_Tva->scpTVA; 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /src/Responses/UBLUploadResponse.php: -------------------------------------------------------------------------------- 1 | rawResponse === null) { 19 | $this->InternalCreateError("No response to parse", ANAFException::EMPTY_RAW_RESPONSE); 20 | return; 21 | } 22 | if ($this->isErrorResponse($this->rawResponse)) { 23 | $this->InternalCreateError("API returned an error", ANAFException::REMOTE_EXCEPTION); 24 | return; 25 | } 26 | try { 27 | if ($this->isJson($this->rawResponse)) { 28 | $this->parseJSON($this->rawResponse); 29 | } else { 30 | $this->parseXML($this->rawResponse); 31 | } 32 | } catch (Throwable $ex) { 33 | $this->LastError = $ex; 34 | } 35 | } 36 | 37 | /** 38 | * Used to check if API response is JSON 39 | * Answers that are JSON are errors 40 | * @param string $string 41 | * @return bool 42 | */ 43 | private function isJson(string $string): bool 44 | { 45 | json_decode($string); 46 | return json_last_error() === JSON_ERROR_NONE; 47 | } 48 | 49 | /** 50 | * Used to check if API response is a specific type of error. Can be indicative of a bad request/invalid input data 51 | * @param string $response 52 | * @return bool 53 | */ 54 | private function isErrorResponse(string $response): bool 55 | { 56 | // Check for specific patterns indicative of an error message 57 | return str_contains($response, 'Your support ID is:'); 58 | } 59 | 60 | /** 61 | * Parse response XML 62 | * @param string $xmlString 63 | * @return void 64 | */ 65 | private function parseXML(string $xmlString): void 66 | { 67 | $xml = simplexml_load_string($xmlString); 68 | $dateResponse = (string)$xml['dateResponse']; 69 | 70 | if ($dateResponse) { 71 | $this->ResponseTimestamp = $this->convertToTimestamp($dateResponse, 'YmdHi'); 72 | } 73 | 74 | $success = ((string)$xml['ExecutionStatus'] === "0"); 75 | 76 | if (isset($xml['index_incarcare'])) { 77 | $this->IndexIncarcare = (string)$xml['index_incarcare']; 78 | } else if ($success) { 79 | $this->InternalCreateError("Eroare necunoscută, nu s-a regăsit index_incarcare în răspunsul ANAF", ANAFException::INCOMPLETE_RESPONSE); 80 | } 81 | if (!$success) { 82 | if ($xml->Errors && isset($xml->Errors['errorMessage'])) { 83 | $this->InternalCreateError((string)$xml->Errors['errorMessage'], ANAFException::REMOTE_EXCEPTION); 84 | } else { 85 | $this->InternalCreateError("Eroare necunoscută: " . $this->rawResponse, ANAFException::INCOMPLETE_RESPONSE); 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * Parse JSON error 92 | * @param string $jsonString 93 | * @return void 94 | */ 95 | private function parseJSON(string $jsonString): void 96 | { 97 | $json = json_decode($jsonString); 98 | if (isset($json->timestamp)) { 99 | $this->ResponseTimestamp = $this->convertToTimestamp($json->timestamp, 'd-m-Y H:i:s'); 100 | } 101 | 102 | // JSON response always results in false execution status 103 | $message = "Unknown error"; 104 | 105 | if (isset($json->error) && isset($json->message)) { 106 | $message = $json->error . ": " . $json->message; 107 | } 108 | $this->InternalCreateError($message, ANAFException::REMOTE_EXCEPTION); 109 | } 110 | 111 | /** 112 | * Convert date string to timestamp 113 | * @param string $dateStr 114 | * @param string $format 115 | * @return int 116 | */ 117 | private function convertToTimestamp(string $dateStr, string $format): int 118 | { 119 | $date = DateTime::createFromFormat($format, $dateStr); 120 | return $date->getTimestamp(); 121 | } 122 | 123 | public static function CreateError(Throwable $error): UBLUploadResponse 124 | { 125 | $result = new UBLUploadResponse(); 126 | $result->LastError = $error; 127 | return $result; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/ParseTests/AnswerListTest.php: -------------------------------------------------------------------------------- 1 | rawResponse = $response; 19 | $answerList->Parse(); 20 | $this->assertTrue($answerList->IsSuccess()); 21 | $this->assertEquals($expected, json_encode($answerList->mesaje), "Unexpected content"); 22 | } catch (Throwable $ex) { 23 | $this->fail("Exception thrown: " . $ex->getMessage()); 24 | } 25 | } 26 | 27 | public function testNoAnswers(): void 28 | { 29 | $response = "{\"eroare\":\"Nu exista mesaje in ultimele 5 zile\",\"titlu\":\"Lista Mesaje\"}"; 30 | try { 31 | $answerList = new ANAFAnswerListResponse(); 32 | $answerList->rawResponse = $response; 33 | $answerList->Parse(); 34 | $this->assertTrue($answerList->IsSuccess(), "Response is not successful"); 35 | $this->assertCount(0, $answerList->mesaje, "Wrong answer count"); 36 | } catch (Throwable $ex) { 37 | $this->fail("Exception thrown: " . $ex->getMessage()); 38 | } 39 | } 40 | 41 | public function testRemoteError(): void 42 | { 43 | $response = "{\"eroare\":\"Numarul de zile trebuie sa fie intre 1 si 60\",\"titlu\":\"Lista Mesaje\"}"; 44 | try { 45 | $answerList = new ANAFAnswerListResponse(); 46 | $answerList->rawResponse = $response; 47 | $answerList->Parse(); 48 | $this->assertTrue($answerList->HasError(), "Response has no error"); 49 | $this->assertEquals(ANAFException::REMOTE_EXCEPTION, $answerList->LastError->getCode(), "Error code mismatch"); 50 | } catch (Throwable $ex) { 51 | $this->fail("Exception thrown: " . $ex->getMessage()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /tests/ParseTests/EntityResponseTest.php: -------------------------------------------------------------------------------- 1 | rawResponse = $response; 19 | $entityResponse->Parse(); 20 | $this->assertTrue($entityResponse->Entity != null, "Entity is null"); 21 | $this->assertFalse($entityResponse->HasError(), "Response has error"); 22 | $this->assertEquals($expected, json_encode($entityResponse->Entity), "Entity unexpected content"); 23 | } catch (Throwable $ex) { 24 | $this->fail("Exception thrown: " . $ex->getMessage()); 25 | } 26 | } 27 | 28 | public function testNotFoundValidCIF(): void 29 | { 30 | $response = "{\"cod\":200,\"message\":\"SUCCESS\",\"found\":[],\"notFound\":[12345678]}"; 31 | try { 32 | $entityResponse = new EntityResponse(); 33 | $entityResponse->rawResponse = $response; 34 | $entityResponse->Parse(); 35 | $this->assertTrue($entityResponse->Entity == null, "Entity is not null"); 36 | $this->assertTrue($entityResponse->HasError(), "Response has no error"); 37 | $this->assertEquals(ANAFException::RESULT_NOT_FOUND, $entityResponse->LastError->getCode(), "Error code mismatch"); 38 | } catch (Throwable $ex) { 39 | $this->fail("Exception thrown: " . $ex->getMessage()); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /tests/ParseTests/UploadResponseTest.php: -------------------------------------------------------------------------------- 1 | \r
"; 15 | try { 16 | $uploadResponse = new UBLUploadResponse(); 17 | $uploadResponse->rawResponse = $response; 18 | $uploadResponse->Parse(); 19 | $this->assertTrue($uploadResponse->IsSuccess()); 20 | $this->assertEquals(1234567890, $uploadResponse->IndexIncarcare, "Wrong index_incarcare"); 21 | $this->assertEquals(1712252460, $uploadResponse->ResponseTimestamp, "Wrong timestamp"); 22 | } catch (Throwable $ex) { 23 | $this->fail("Exception thrown: " . $ex->getMessage()); 24 | } 25 | } 26 | 27 | public function testBadResponse(): void 28 | { 29 | $response = "\r
\r \r
"; 30 | try { 31 | $uploadResponse = new UBLUploadResponse(); 32 | $uploadResponse->rawResponse = $response; 33 | $uploadResponse->Parse(); 34 | $this->assertTrue($uploadResponse->HasError(), "Response does not have error"); 35 | $this->assertEquals(ANAFException::REMOTE_EXCEPTION, $uploadResponse->LastError->getCode(), "Error code mismatch"); 36 | } catch (Throwable $ex) { 37 | $this->fail("Exception thrown: " . $ex->getMessage()); 38 | } 39 | } 40 | 41 | public function testBadRequest() 42 | { 43 | $response = "{\r \"timestamp\": \"05-08-2021 12:04:01\",\r \"status\": 400,\r \"error\": \"Bad Request\",\r \"message\": \"Trebuie sa aveti atasat in request un fisier de tip xml\"\r}"; 44 | try { 45 | $uploadResponse = new UBLUploadResponse(); 46 | $uploadResponse->rawResponse = $response; 47 | $uploadResponse->Parse(); 48 | $this->assertTrue($uploadResponse->HasError(), "Response does not have error"); 49 | $this->assertEquals(ANAFException::REMOTE_EXCEPTION, $uploadResponse->LastError->getCode(), "Error code mismatch"); 50 | } catch (Throwable $ex) { 51 | $this->fail("Exception thrown: " . $ex->getMessage()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /tests/ParseTests/VerifyResponseTest.php: -------------------------------------------------------------------------------- 1 | rawResponse = $response; 17 | $verifyResponse->Parse(); 18 | $this->assertTrue($verifyResponse->IsSuccess(), "Response is not successful"); 19 | $this->assertTrue($verifyResponse->IsOK(), "Response is not OK"); 20 | } catch (Throwable $ex) { 21 | $this->fail("Exception thrown: " . $ex->getMessage()); 22 | } 23 | } 24 | 25 | public function testNotOkResponse(): void 26 | { 27 | $response = "{\"stare\":\"nok\",\"Messages\":[{\"message\":\"tipAssert=FailedAssert; codEroare=UBL-CR-661; localizareEroare=/Invoice; textEroare=[UBL-CR-661]-A UBL invoice should not include the PaymentMeansCode listID; expresieValidata=not(cac:PaymentMeans/cbc:PaymentMeansCode/@listID)\"},{\"message\":\"tipAssert=FailedAssert; codEroare=UBL-DT-17; localizareEroare=/Invoice; textEroare=[UBL-DT-17]-List name attribute should not be present; expresieValidata=not(//@listName)\"},{\"message\":\"tipAssert=FailedAssert; codEroare=UBL-DT-20; localizareEroare=/Invoice; textEroare=[UBL-DT-20]-List uri attribute should not be present; expresieValidata=not(//@listURI)\"}],\"trace_id\":\"0346459f-bd4b-48de-ac8e-cdb8d8a6c6fd\"}"; 28 | try { 29 | $verifyResponse = new ANAFVerifyResponse(); 30 | $verifyResponse->rawResponse = $response; 31 | $verifyResponse->Parse(); 32 | $this->assertTrue($verifyResponse->IsSuccess(), "Response is not successful"); 33 | $this->assertFalse($verifyResponse->IsOK(), "Response is OK, it should not be"); 34 | $this->assertIsArray($verifyResponse->Messages, "Messages is not an array"); 35 | $this->assertCount(3, $verifyResponse->Messages, "Messages count mismatch"); 36 | } catch (Throwable $ex) { 37 | $this->fail("Exception thrown: " . $ex->getMessage()); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/RequestTests/AccessTokenTest.php: -------------------------------------------------------------------------------- 1 | assertFileExists(__DIR__ . '/../../src/ANAFAccessToken.json', "No access token file found"); 10 | $client = $this->createClient(); 11 | $this->assertTrue($client->HasAccessToken(), "Failed to read access token"); 12 | } 13 | } -------------------------------------------------------------------------------- /tests/RequestTests/AnswerListAnswerTest.php: -------------------------------------------------------------------------------- 1 | createClient(); 16 | $response = $client->ListAnswers($this->cif, 1); 17 | $localParsed = json_decode($response->rawResponse); 18 | } catch (Throwable $ex) { 19 | $this->fail("Exception thrown: " . $ex->getMessage()); 20 | } 21 | $this->assertTrue(json_last_error() == JSON_ERROR_NONE, "Invalid JSON response"); 22 | $this->assertTrue($response->IsSuccess(), "Response is not successful"); 23 | 24 | if (isset($localParsed->mesaje)) { 25 | $this->markTestSkipped("Response was not empty"); 26 | } else { 27 | $this->assertTrue(isset($localParsed->titlu), "Invalid response: title not set"); 28 | $this->assertEquals("lista mesaje", strtolower($localParsed->titlu), "Invalid response: title mismatch"); 29 | $this->assertTrue(isset($localParsed->eroare), "Invalid response: error not set"); 30 | $this->assertStringContainsString("nu exista mesaje", strtolower($localParsed->eroare), "Invalid response: error mismatch"); 31 | $this->assertFalse(isset($localParsed->cui), "Invalid response: CUI should not be set"); 32 | $this->assertFalse(isset($localParsed->serial), "Invalid response: serial should not be set"); 33 | $this->assertTrue($response->IsSuccess(), "Response not successful"); 34 | $this->assertCount(0, $response->mesaje, "Messages count mismatch"); 35 | } 36 | } 37 | 38 | public function testValidResponseCheck() 39 | { 40 | if (getenv("ANAF_EMPTY_LIST_ONLY") == "true") { 41 | $this->markTestSkipped("ANAF_EMPTY_ONLY is set, skipping"); 42 | } 43 | $validUBL = base64_decode(UploadUBLTest::VALID_UBL); 44 | $listAnswer = null; 45 | try { 46 | $client = $this->createClient(); 47 | $result = $client->UploadEFactura($validUBL, $this->cif); 48 | $this->assertTrue($result->IsSuccess(), "Test upload failed"); 49 | sleep(1); 50 | $listAnswer = $client->ListAnswers($this->cif, 1); 51 | } catch (Throwable $ex) { 52 | $this->fail("Exception thrown: " . $ex->getMessage()); 53 | } 54 | $this->assertNotNull($listAnswer, "No answer found"); 55 | $this->assertTrue($listAnswer->IsSuccess(), "Failed to list answers"); 56 | $this->assertIsArray($listAnswer->mesaje, "Messages is not an array"); 57 | $this->assertGreaterThan(0, count($listAnswer->mesaje), "No messages found"); 58 | } 59 | 60 | public function testAnswersListWithPages() 61 | { 62 | $client = $this->createClient(); 63 | $answers = $client->ListAnswers($this->cif, 10); 64 | $this->assertTrue($answers->IsSuccess()); 65 | $this->assertIsArray($answers->mesaje); 66 | $startTime = new DateTime(); 67 | $startTime->sub(new DateInterval("P10D")); 68 | /** @noinspection PhpRedundantOptionalArgumentInspection */ 69 | $startTime->setTime(0, 0, 0); 70 | $endTime = new DateTime(); 71 | $endTime->sub(new DateInterval("PT60S")); 72 | $pagedAnswers = $client->ListAnswersWithPagination($startTime->getTimestamp(), $endTime->getTimestamp(), $this->cif); 73 | $errorMessage = $pagedAnswers->IsSuccess() ? "" : $pagedAnswers->LastError->getMessage(); 74 | $this->assertTrue($pagedAnswers->IsSuccess(), $errorMessage); 75 | $this->assertIsArray($pagedAnswers->mesaje); 76 | for ($i = 0; $i < count($pagedAnswers->mesaje); $i++) { 77 | $paged = $pagedAnswers->mesaje[$i]; 78 | $found = false; 79 | for ($j = 0; $j < count($answers->mesaje); $j++) { 80 | $original = $answers->mesaje[$j]; 81 | if ($original->id == $paged->id) { 82 | $found = true; 83 | $this->assertEquals($original->detalii, $paged->detalii); 84 | break 1; 85 | } 86 | } 87 | $this->assertTrue($found, "Message not found in original list"); 88 | } 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /tests/RequestTests/RequestTestBase.php: -------------------------------------------------------------------------------- 1 | 'test-anaf-client-id', 15 | 'clientSecret' => 'test-anaf-client-secret', 16 | 'redirectUri' => 'https://test.com/callback.php', 17 | 'urlAuthorize' => 'https://logincert.anaf.ro/anaf-oauth2/v1/authorize', 18 | 'urlAccessToken' => 'https://logincert.anaf.ro/anaf-oauth2/v1/token', 19 | 'urlResourceOwnerDetails' => 'https://logincert.anaf.ro/anaf-oauth2/v1/resource' 20 | ]; 21 | 22 | protected function setUp(): void 23 | { 24 | if (getenv('TEST_ANAF_REQUESTS') !== 'true') { 25 | $this->markTestSkipped('ANAF requests are disabled'); 26 | } elseif (empty(getenv('TEST_ANAF_CLIENT_CIF'))) { 27 | $this->fail('TEST_ANAF_CLIENT_CIF is not set'); 28 | } else if (!ANAFAPIClient::ValidateCIF(getenv("TEST_ANAF_CLIENT_CIF"))) { 29 | $this->fail('Invalid CIF'); 30 | } 31 | $cif = getenv('TEST_ANAF_CLIENT_CIF'); 32 | $this->setCif($cif); 33 | parent::setUp(); 34 | } 35 | 36 | private function setCif(string $cif): void 37 | { 38 | if (is_numeric($cif)) { 39 | $this->cif = intval($cif); 40 | } else { 41 | if (str_starts_with(strtolower($cif), "ro")) { 42 | $cif = substr($cif, 2); 43 | $this->setCif($cif); 44 | } else { 45 | $this->fail("Failed to strip CIF: $cif"); 46 | } 47 | } 48 | } 49 | 50 | protected function createClient(callable|null $errorCallback = null): ANAFAPIClient 51 | { 52 | $classRef = new ReflectionClass(ANAFAPIClient::class); 53 | 54 | if ($errorCallback === null) { 55 | $errorCallback = function (string $message, ?Throwable $exception = null) { 56 | if ($exception !== null) { 57 | $message .= "\n" . $exception->getMessage() . "\n" . $exception->getTraceAsString(); 58 | } 59 | $this->fail($message); 60 | }; 61 | } 62 | $client = new ANAFAPIClient(self::ANAF_OAUTH, false, $errorCallback); 63 | $this->assertFalse($client->IsProduction()); 64 | $classRef->getProperty("LockToken")->setValue($client, true); 65 | return $client; 66 | } 67 | } -------------------------------------------------------------------------------- /tests/RequestTests/UploadUBLTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped("ANAF_EMPTY_ONLY is set, skipping"); 16 | } 17 | try { 18 | $client = $this->createClient(); 19 | $ubl = base64_decode(UploadUBLTest::VALID_UBL); 20 | $response = $client->UploadEFactura($ubl, "" . $this->cif); 21 | } catch (Throwable $ex) { 22 | $this->fail("Exception thrown: " . $ex->getMessage()); 23 | } 24 | $this->assertTrue($response->IsSuccess(), "Failed to upload UBL"); 25 | $this->assertIsNumeric($response->IndexIncarcare); 26 | $this->assertGreaterThan(0, $response->IndexIncarcare); 27 | $this->assertIsInt($response->ResponseTimestamp); 28 | $this->assertGreaterThan(0, $response->ResponseTimestamp); 29 | } 30 | } --------------------------------------------------------------------------------