diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..86b871d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,39 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ tests:
+ name: PHP ${{ matrix.php }} Tests
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ php: ['8.3']
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: curl, json, mbstring
+ coverage: xdebug
+ tools: composer:v2
+
+ - name: Validate composer.json
+ run: composer validate --strict
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ - name: Run PHPStan
+ run: vendor/bin/phpstan analyse
+
+ - name: Run PHPUnit
+ run: vendor/bin/phpunit --coverage-text
diff --git a/.gitignore b/.gitignore
index f844c0b..e93bafc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,22 @@ vendor/
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
-nbproject/
\ No newline at end of file
+nbproject/
+
+# Testing
+var/
+.phpunit.result.cache
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Docker
+.env
+docker-compose.override.yml
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..43fa971
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+FROM php:8.3-cli-alpine
+
+RUN apk add --no-cache \
+ bash \
+ git \
+ unzip \
+ $PHPIZE_DEPS
+
+RUN pecl install pcov \
+ && docker-php-ext-enable pcov
+
+COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+
+WORKDIR /app
diff --git a/composer.json b/composer.json
index 6b91f1f..cf1206b 100644
--- a/composer.json
+++ b/composer.json
@@ -14,12 +14,27 @@
}
],
"require": {
- "php": ">=5.4.0",
+ "php": ">=8.3.0",
"datingvip/curl": "^2.0"
},
+ "require-dev": {
+ "phpstan/extension-installer": "^1.3",
+ "phpstan/phpstan": "^2.0",
+ "phpunit/phpunit": "^11.0"
+ },
"autoload": {
- "psr-0": {
- "DatingVIP": "src"
+ "psr-4": {
+ "DatingVIP\\API\\": "src/DatingVIP/API/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "DatingVIP\\API\\Tests\\": "tests/"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "phpstan/extension-installer": true
}
}
}
diff --git a/composer.lock b/composer.lock
index 79e5560..1161a26 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "237dac62cfeb9dca473976438156c69d",
+ "content-hash": "47410a1be2e74c10735bb67efdd693f3",
"packages": [
{
"name": "datingvip/curl",
@@ -46,17 +46,1891 @@
"keywords": [
"curl"
],
+ "support": {
+ "source": "https://github.com/DatingVIP/cURL/tree/master"
+ },
"time": "2016-06-24T20:27:09+00:00"
}
],
- "packages-dev": [],
+ "packages-dev": [
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
+ "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
+ },
+ "time": "2025-10-21T19:32:17+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0 || ^2.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.4.3"
+ },
+ "time": "2024-09-04T20:21:43+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.32",
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227",
+ "reference": "e126cad1e30a99b137b8ed75a85a676450ebb227",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-11T15:18:17+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4",
+ "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.4.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-text-template": "^4.0.1",
+ "sebastian/code-unit-reverse-lookup": "^4.0.1",
+ "sebastian/complexity": "^4.0.1",
+ "sebastian/environment": "^7.2.0",
+ "sebastian/lines-of-code": "^3.0.1",
+ "sebastian/version": "^5.0.2",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.2"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-27T14:37:49+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-27T05:02:59+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:07:44+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:08:43+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:09:35+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.5.45",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "faf5fff4fb9beb290affa53f812b05380819c51a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/faf5fff4fb9beb290affa53f812b05380819c51a",
+ "reference": "faf5fff4fb9beb290affa53f812b05380819c51a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0.11",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-invoker": "^5.0.1",
+ "phpunit/php-text-template": "^4.0.1",
+ "phpunit/php-timer": "^7.0.1",
+ "sebastian/cli-parser": "^3.0.2",
+ "sebastian/code-unit": "^3.0.3",
+ "sebastian/comparator": "^6.3.2",
+ "sebastian/diff": "^6.0.2",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/exporter": "^6.3.2",
+ "sebastian/global-state": "^7.0.2",
+ "sebastian/object-enumerator": "^6.0.1",
+ "sebastian/type": "^5.1.3",
+ "sebastian/version": "^5.0.2",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.45"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-01T07:38:43+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:41:36+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-19T07:56:08+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:45:54+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8",
+ "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.4"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T08:07:46+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:49:50+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-05-21T11:55:47+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:12:51+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:57:36+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:58:38+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:00:13+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:01:32+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T04:42:22+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-09T06:55:48+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T05:16:32+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-17T20:03:58+00:00"
+ }
+ ],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=5.4.0"
+ "php": ">=8.3.0"
},
- "platform-dev": []
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..21dc58c
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '3.8'
+
+services:
+ php:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ volumes:
+ - .:/app
+ working_dir: /app
+ environment:
+ - PCOV_ENABLED=1
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..976e917
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,8 @@
+parameters:
+ level: 6
+ paths:
+ - src
+ excludePaths:
+ - vendor
+ tmpDir: var/phpstan
+ treatPhpDocTypesAsCertain: false
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..6582f33
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ tests
+
+
+
+
+ src
+
+
+
+
+
+
+
+
+
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..7229b5d
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+OUTPUT_FILE="/tmp/api-client-test-$(date +%Y%m%d-%H%M%S).log"
+
+echo "Building Docker image..."
+docker-compose build php
+
+echo "Running tests... Output will be saved to $OUTPUT_FILE"
+
+docker-compose run --rm php vendor/bin/phpunit "$@" 2>&1 | tee "$OUTPUT_FILE"
+
+echo ""
+echo "Test output saved to: $OUTPUT_FILE"
diff --git a/src/DatingVIP/API/Client.php b/src/DatingVIP/API/Client.php
index f1d5506..9138f74 100644
--- a/src/DatingVIP/API/Client.php
+++ b/src/DatingVIP/API/Client.php
@@ -1,4 +1,7 @@
* @copyright All rights reserved
*
- * @version 1.0
+ * @version 2.0
*/
namespace DatingVIP\API;
@@ -19,113 +22,63 @@ class Client
{
const COMMAND = 'cmd';
- /**
- * API URL.
- *
- * @var string
- */
- protected $url;
+ protected string $url = '';
- /**
- * API authorization user.
- *
- * @var string
- */
- protected $user;
+ protected string $user = '';
- /**
- * API authorization pass.
- *
- * @var string
- */
- protected $pass;
+ protected string $pass = '';
/**
- * API authorization pass.
- *
- * @var int [= 5]
+ * Request timeout in seconds.
*/
- protected $timeout = 5;
+ protected int $timeout = 5;
/**
- * Cookie storage path
- *
- * @var int $cookie [= false]
- * @access protected
+ * Cookie storage path.
*/
- protected $cookie = false;
+ protected string|false $cookie = false;
- /**
- * User-Agent string
- *
- * @var string $user_agent [= 'DatingVIP-API/1.0.10']
- * @access protected
- */
- protected $user_agent = 'DatingVIP-API/1.0.10';
+ protected string $user_agent = 'DatingVIP-API/2.0.0';
- /**
- * Instance of DatingVIP\cURL\Request lib.
- *
- * @var Request
- */
- private $curl;
+ private ?cURL $curl = null;
/**
* Set API url.
- *
- * @param string $url
- *
- * @return bool
*/
- public function setUrl($url)
+ public function setUrl(string $url): bool
{
- $this->url = (string) $url;
+ $this->url = $url;
return !empty($this->url);
}
/**
* Set auth data for API.
- *
- * @param string $user
- * @param string $pass
- *
- * @return bool
*/
- public function setAuth($user, $pass)
+ public function setAuth(string $user, string $pass): bool
{
- $this->user = (string) $user;
- $this->pass = (string) $pass;
+ $this->user = $user;
+ $this->pass = $pass;
return $this->hasAuth();
}
/**
* Set request timeout value (in seconds).
- *
- * @param int $timeout
- *
- * @return int
*/
- public function setTimeout($timeout)
+ public function setTimeout(int $timeout): int
{
- $timeout = is_scalar($timeout) ? (int) $timeout : 0;
-
return $timeout < 1
? $this->timeout
: $this->timeout = $timeout;
}
/**
- * Set cookie storage file
- *
- * @param string $file
- * @access public
- * @return bool
+ * Set cookie storage file.
*/
- public function setCookieStorage($file)
+ public function setCookieStorage(string $file): bool
{
- $this->cookie = (string) $file;
+ $this->cookie = $file;
return !empty($this->cookie);
}
@@ -133,13 +86,9 @@ public function setCookieStorage($file)
/**
* Execute API command.
*
- * @param Command $command
- *
- * @return \DatingVIP\API\Response
- *
- * @throws \Exception
+ * @throws Exception
*/
- public function execute(Command $command)
+ public function execute(Command $command): Response
{
if (!$command->isValid()) {
throw new Exception('Invalid API command supplied');
@@ -153,12 +102,8 @@ public function execute(Command $command)
/**
* Return expected response type based on URL.
- *
- * @param void
- *
- * @return string
*/
- private function getResponseType()
+ private function getResponseType(): string
{
if (false === strpos($this->url, '.')) {
return "";
@@ -172,12 +117,8 @@ private function getResponseType()
* Get browser for making API requests
* - create an instance
* - assign auth data if we have it.
- *
- * @param void
- *
- * @return cURL
*/
- private function curl()
+ private function curl(): cURL
{
if (!($this->curl instanceof cURL)) {
$this->curl = new cURL();
@@ -187,7 +128,7 @@ private function curl()
$this->curl->setCredentials($this->user, $this->pass);
}
- if ($this->cookie && is_writeable(dirname($this->cookie))) {
+ if ($this->cookie && is_writable(dirname($this->cookie))) {
$this->curl->setCookieStorage($this->cookie);
}
@@ -200,12 +141,8 @@ private function curl()
/**
* Get API URL for given command.
- *
- * @param Command $command
- *
- * @return string
*/
- protected function getUrl(Command $command)
+ protected function getUrl(Command $command): string
{
return $this->url
.(stripos($this->url, '?') !== false ? '&' : '?')
@@ -215,12 +152,8 @@ protected function getUrl(Command $command)
/**
* Check if API has auth data set
* - checks if user and pass aren't empty.
- *
- * @param void
- *
- * @return bool
*/
- private function hasAuth()
+ private function hasAuth(): bool
{
return !empty($this->user) && !empty($this->pass);
}
diff --git a/src/DatingVIP/API/Command.php b/src/DatingVIP/API/Command.php
index 8eda577..273b392 100644
--- a/src/DatingVIP/API/Command.php
+++ b/src/DatingVIP/API/Command.php
@@ -1,4 +1,7 @@
* @copyright All rights reserved
*
- * @version 1.0
+ * @version 2.0
*/
namespace DatingVIP\API;
@@ -16,28 +19,20 @@ class Command
{
const VAR_CONTROLLER = 'c';
- /**
- * Command name.
- *
- * @var string
- */
- private $name;
+ private string $name = '';
/**
- * Command data.
- *
- * @var array
+ * @var array
*/
- private $data;
+ private array $data = [];
/**
* Default command constructor
* - shorthand to set method.
*
- * @param string $name [= '']
- * @param array $data [= []]
+ * @param array $data
*/
- public function __construct($name = '', array $data = [])
+ public function __construct(string $name = '', array $data = [])
{
$this->set($name, $data);
}
@@ -46,12 +41,9 @@ public function __construct($name = '', array $data = [])
* Set command and optionally set data
* - return if set command is valid.
*
- * @param string $name
- * @param array $data [= []]
- *
- * @return bool
+ * @param array $data
*/
- public function set($name, array $data = [])
+ public function set(string $name, array $data = []): bool
{
$this->setName($name);
$this->setData($data);
@@ -61,38 +53,28 @@ public function set($name, array $data = [])
/**
* Get command name.
- *
- * @param void
- *
- * @return string
*/
- public function getName()
+ public function getName(): string
{
- return (string) $this->name;
+ return $this->name;
}
/**
* Get command data.
*
- * @param void
- *
- * @return array
+ * @return array
*/
- public function getData()
+ public function getData(): array
{
- return (array) $this->data;
+ return $this->data;
}
/**
* Check if set name is considered valid
* - must not be empty
* - must have a dot.
- *
- * @param void
- *
- * @return bool
*/
- public function isValid()
+ public function isValid(): bool
{
return !empty($this->name) && strpos($this->name, '.') !== false;
}
@@ -100,14 +82,10 @@ public function isValid()
/**
* Set command name
* - return if set name is considered valid.
- *
- * @param string $name
- *
- * @return bool
*/
- private function setName($name)
+ private function setName(string $name): bool
{
- $this->name = (string) $name;
+ $this->name = $name;
return $this->isValid();
}
@@ -115,15 +93,12 @@ private function setName($name)
/**
* Set command data.
*
- * @param array $data
- *
- * @return bool
+ * @param array $data
*/
- private function setData(array $data)
+ private function setData(array $data): bool
{
- $this->data = (array) $data;
+ $this->data = $data;
- // remove reserved stuff
if (isset($this->data[self::VAR_CONTROLLER])) {
unset($this->data[self::VAR_CONTROLLER]);
}
diff --git a/src/DatingVIP/API/Response.php b/src/DatingVIP/API/Response.php
index e9ae0e3..e773e56 100644
--- a/src/DatingVIP/API/Response.php
+++ b/src/DatingVIP/API/Response.php
@@ -1,4 +1,7 @@
* @copyright All rights reserved
*
- * @version 1.0
+ * @version 2.0
*/
namespace DatingVIP\API;
-use RuntimeException;
-
class Response
{
const KEY_RESULT = 'result';
@@ -21,32 +22,24 @@ class Response
const KEY_META = 'meta';
/**
- * Array holding response data.
- *
- * @var array [= []]
+ * @var array
*/
- private $data = [];
+ private array $data = [];
- /**
- * Format of response.
- *
- * @var string [= 'json']
- */
- private $format = 'json';
+ private string $format = 'json';
- private $response = '';
+ private string $response = '';
- private $error = [];
+ /**
+ * @var array
+ */
+ private array $error = [];
/**
* Default API response constructor
* - optionally set response.
- *
- * @param mixed $data [= '']
- * @param mixed $format [= 'json']
- * @throws RuntimeException
*/
- public function __construct($data = '', $format = 'json')
+ public function __construct(string $data = '', string $format = 'json')
{
if (!empty($format)) {
$this->format = $format;
@@ -57,12 +50,8 @@ public function __construct($data = '', $format = 'json')
/**
* Get API response.
- *
- * @param void
- *
- * @return string
*/
- public function get()
+ public function get(): string
{
$result = null;
switch ($this->format) {
@@ -79,27 +68,32 @@ public function get()
return $result;
}
- public function getRawResponse () {
+ public function getRawResponse(): string
+ {
return $this->response;
}
- public function getRawError () {
+ /**
+ * @return array
+ */
+ public function getRawError(): array
+ {
return $this->error;
}
- public function getRawData () {
+ /**
+ * @return array
+ */
+ public function getRawData(): array
+ {
return $this->data;
}
/**
* Set API response
* - decode from JSON.
- *
- * @param string $data
- * @throws RuntimeException
- * @return bool
*/
- public function set($data)
+ public function set(string $data): bool
{
$this->error = [];
$result = null;
@@ -111,9 +105,9 @@ public function set($data)
case 'json':
default:
- $this->data = empty($data) ? [] : (json_decode((string) $data, true) ?: []);
- $result = json_last_error() == JSON_ERROR_NONE;
- if (empty ($result)) {
+ $this->data = empty($data) ? [] : (json_decode($data, true) ?: []);
+ $result = json_last_error() === JSON_ERROR_NONE;
+ if (empty($result)) {
$this->error = [
'error' => json_last_error(),
'msg' => json_last_error_msg(),
@@ -127,365 +121,240 @@ public function set($data)
/**
* Set result.
- *
- * @param mixed $data
- *
- * @return mixed
*/
- public function setResult($data)
+ public function setResult(mixed $data): mixed
{
return $this->data[self::KEY_RESULT] = $data;
}
/**
* Set texts (translations).
- *
- * @param mixed $data
- *
- * @return mixed
*/
- public function setTexts($data)
+ public function setTexts(mixed $data): mixed
{
return $this->data[self::KEY_TEXTS] = $data;
}
/**
* Set status.
- *
- * @param status $status
- *
- * @return mixed
*/
- public function setStatus($status)
+ public function setStatus(mixed $status): mixed
{
return $this->setMeta(__FUNCTION__, $status);
}
/**
* Set errors.
- *
- * @param mixed $errors
- *
- * @return mixed
*/
- public function setErrors($errors)
+ public function setErrors(mixed $errors): mixed
{
return $this->setMeta(__FUNCTION__, $errors);
}
/**
- * Set result.
- *
- * @param mixed $messages
- *
- * @return mixed
+ * Set messages.
*/
- public function setMessages($messages)
+ public function setMessages(mixed $messages): mixed
{
return $this->setMeta(__FUNCTION__, $messages);
}
/**
* Set warnings.
- *
- * @param mixed $warnings
- *
- * @return mixed
*/
- public function setWarnings($warnings)
+ public function setWarnings(mixed $warnings): mixed
{
return $this->setMeta(__FUNCTION__, $warnings);
}
/**
* Set announcements.
- *
- * @param mixed $announcements
- *
- * @return mixed
*/
- public function setAnnouncements($announcements)
+ public function setAnnouncements(mixed $announcements): mixed
{
return $this->setMeta(__FUNCTION__, $announcements);
}
/**
* Set application (developer) errors.
- *
- * @param mixed $app_errors
- *
- * @return mixed
*/
- public function setAppErrors($app_errors)
+ public function setAppErrors(mixed $app_errors): mixed
{
return $this->setMeta(str_replace('Errors', '', __FUNCTION__), $app_errors);
}
/**
* Get result.
- *
- * @param void
- *
- * @return mixed
*/
- public function getResult()
+ public function getResult(): mixed
{
return $this->hasResult() ? $this->data[self::KEY_RESULT] : null;
}
/**
* Get texts.
- *
- * @param void
- *
- * @return mixed
*/
- public function getTexts()
+ public function getTexts(): mixed
{
return $this->hasTexts() ? $this->data[self::KEY_TEXTS] : null;
}
/**
* Get status.
- *
- * @param void
- *
- * @return mixed
*/
- public function getStatus()
+ public function getStatus(): mixed
{
return $this->getMeta(__FUNCTION__);
}
/**
* Get errors.
- *
- * @param void
- *
- * @return mixed
*/
- public function getErrors()
+ public function getErrors(): mixed
{
return $this->getMeta(__FUNCTION__);
}
/**
* Get messages.
- *
- * @param void
- *
- * @return mixed
*/
- public function getMessages()
+ public function getMessages(): mixed
{
return $this->getMeta(__FUNCTION__);
}
/**
* Get warnings.
- *
- * @param void
- *
- * @return mixed
*/
- public function getWarnings()
+ public function getWarnings(): mixed
{
return $this->getMeta(__FUNCTION__);
}
/**
* Get announcements.
- *
- * @param void
- *
- * @return mixed
*/
- public function getAnnouncements()
+ public function getAnnouncements(): mixed
{
return $this->getMeta(__FUNCTION__);
}
/**
* Get application (developer) errors.
- *
- * @param void
- *
- * @return mixed
*/
- public function getAppErrors()
+ public function getAppErrors(): mixed
{
return $this->getMeta(str_replace('Errors', '', __FUNCTION__));
}
/**
* Do we have result?
- *
- * @param void
- *
- * @return bool
*/
- public function hasResult()
+ public function hasResult(): bool
{
return array_key_exists(self::KEY_RESULT, $this->data);
}
/**
* Do we have translations (texts)?
- *
- * @param void
- *
- * @return bool
*/
- public function hasTexts()
+ public function hasTexts(): bool
{
return array_key_exists(self::KEY_TEXTS, $this->data);
}
/**
* Do we have errors?
- *
- * @param void
- *
- * @return bool
*/
- public function hasErrors()
+ public function hasErrors(): bool
{
return $this->hasMeta(__FUNCTION__);
}
/**
* Do we have messages?
- *
- * @param void
- *
- * @return bool
*/
- public function hasMessages()
+ public function hasMessages(): bool
{
return $this->hasMeta(__FUNCTION__);
}
/**
* Do we have warnings?
- *
- * @param void
- *
- * @return bool
*/
- public function hasWarnings()
+ public function hasWarnings(): bool
{
return $this->hasMeta(__FUNCTION__);
}
/**
* Do we have announcements?
- *
- * @param void
- *
- * @return bool
*/
- public function hasAnnouncements()
+ public function hasAnnouncements(): bool
{
return $this->hasMeta(__FUNCTION__);
}
/**
* Do we have application (developer) errors?
- *
- * @param void
- *
- * @return bool
*/
- public function hasAppErrors()
+ public function hasAppErrors(): bool
{
return $this->hasMeta(str_replace('Errors', '', __FUNCTION__));
}
/**
* Set some type of meta data into response.
- *
- * @param string $where
- * @param mixed $data
- *
- * @return bool
*/
- private function setMeta($where, $data)
+ private function setMeta(string $where, mixed $data): mixed
{
return $this->setInData(self::KEY_META, $this->methodToKey($where), $data);
}
/**
* Get some type of meta data shorthand methods.
- *
- * @param string $what
- *
- * @return bool
*/
- private function getMeta($what)
+ private function getMeta(string $what): mixed
{
return $this->getFromData(self::KEY_META, $this->methodToKey($what));
}
/**
* Check existence of some type of meta data shorthand methods.
- *
- * @param string $what
- *
- * @return bool
*/
- private function hasMeta($what)
+ private function hasMeta(string $what): bool
{
return $this->hasData(self::KEY_META, $this->methodToKey($what));
}
/**
* Convert method name to apropriate data key.
- *
- * @param string $method
- *
- * @return string
*/
- private function methodToKey($method)
+ private function methodToKey(string $method): string
{
return strtolower(substr($method, 3));
}
/**
* Get specific type of data from specific key from response.
- *
- * @param string $type
- * @param string $key
- *
- * @return mixed
*/
- private function getFromData($type, $key)
+ private function getFromData(string $type, string $key): mixed
{
return $this->hasData($type, $key) ? $this->data[$type][$key] : null;
}
/**
* Check existence of specific type of data from specific key from response.
- *
- * @param string $type
- * @param string $key
- *
- * @return mixed
*/
- private function hasData($type, $key)
+ private function hasData(string $type, string $key): bool
{
return isset($this->data[$type]) && isset($this->data[$type][$key]);
}
/**
* Set specific category of data to specific key within response.
- *
- * @param string $type
- * @param string $key
- * @param mixed $data
- *
- * @return mixed
- */
- private function setInData($type, $key, $data)
+ */
+ private function setInData(string $type, string $key, mixed $data): mixed
{
return $this->data[$type][$key] = $data;
}
diff --git a/tests/ClientTest.php b/tests/ClientTest.php
new file mode 100644
index 0000000..9d96d00
--- /dev/null
+++ b/tests/ClientTest.php
@@ -0,0 +1,192 @@
+setUrl('https://api.example.com/endpoint');
+
+ $this->assertTrue($result);
+ }
+
+ public function testSetUrlReturnsFalseForEmptyString(): void
+ {
+ $client = new Client();
+ $result = $client->setUrl('');
+
+ $this->assertFalse($result);
+ }
+
+ public function testSetAuth(): void
+ {
+ $client = new Client();
+ $result = $client->setAuth('username', 'password');
+
+ $this->assertTrue($result);
+ }
+
+ public function testSetAuthReturnsFalseWhenIncomplete(): void
+ {
+ $client = new Client();
+
+ $result1 = $client->setAuth('', 'password');
+ $this->assertFalse($result1);
+
+ $result2 = $client->setAuth('username', '');
+ $this->assertFalse($result2);
+
+ $result3 = $client->setAuth('', '');
+ $this->assertFalse($result3);
+ }
+
+ public function testSetTimeout(): void
+ {
+ $client = new Client();
+ $result = $client->setTimeout(30);
+
+ $this->assertSame(30, $result);
+ }
+
+ public function testSetTimeoutReturnsDefaultForZero(): void
+ {
+ $client = new Client();
+ $result = $client->setTimeout(0);
+
+ $this->assertSame(5, $result);
+ }
+
+ public function testSetTimeoutReturnsDefaultForNegative(): void
+ {
+ $client = new Client();
+ $result = $client->setTimeout(-10);
+
+ $this->assertSame(5, $result);
+ }
+
+ public function testSetCookieStorage(): void
+ {
+ $client = new Client();
+ $result = $client->setCookieStorage('/tmp/cookies.txt');
+
+ $this->assertTrue($result);
+ }
+
+ public function testSetCookieStorageReturnsFalseForEmpty(): void
+ {
+ $client = new Client();
+ $result = $client->setCookieStorage('');
+
+ $this->assertFalse($result);
+ }
+
+ public function testExecuteThrowsExceptionForInvalidCommand(): void
+ {
+ $client = new Client();
+ $client->setUrl('https://api.example.com');
+
+ $invalidCommand = new Command('invalid');
+
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('Invalid API command supplied');
+
+ $client->execute($invalidCommand);
+ }
+
+ public function testExecuteWithValidCommand(): void
+ {
+ $client = new Client();
+ $client->setUrl('https://api.example.com/api.json');
+ $command = new Command('test.method', ['param' => 'value']);
+
+ $this->expectException(RuntimeException::class);
+ $client->execute($command);
+ }
+
+ public function testGetUrlWithCommand(): void
+ {
+ $client = new class extends Client {
+ public function testGetUrl(Command $command): string
+ {
+ return $this->getUrl($command);
+ }
+ };
+
+ $client->setUrl('https://api.example.com/endpoint');
+ $command = new Command('controller.action');
+
+ $url = $client->testGetUrl($command);
+
+ $this->assertStringContainsString('https://api.example.com/endpoint', $url);
+ $this->assertStringContainsString('cmd=controller.action', $url);
+ }
+
+ public function testGetUrlAddsQuestionMarkWhenNoQueryString(): void
+ {
+ $client = new class extends Client {
+ public function testGetUrl(Command $command): string
+ {
+ return $this->getUrl($command);
+ }
+ };
+
+ $client->setUrl('https://api.example.com/endpoint');
+ $command = new Command('test.method');
+
+ $url = $client->testGetUrl($command);
+
+ $this->assertStringContainsString('?cmd=', $url);
+ }
+
+ public function testGetUrlAddsAmpersandWhenQueryStringExists(): void
+ {
+ $client = new class extends Client {
+ public function testGetUrl(Command $command): string
+ {
+ return $this->getUrl($command);
+ }
+ };
+
+ $client->setUrl('https://api.example.com/endpoint?param=value');
+ $command = new Command('test.method');
+
+ $url = $client->testGetUrl($command);
+
+ $this->assertStringContainsString('&cmd=', $url);
+ }
+
+ public function testMultipleSettersChaining(): void
+ {
+ $client = new Client();
+
+ $client->setUrl('https://api.example.com');
+ $client->setAuth('user', 'pass');
+ $client->setTimeout(60);
+ $client->setCookieStorage('/tmp/cookies.txt');
+
+ $this->assertTrue(true);
+ }
+
+ public function testUserAgentVersion(): void
+ {
+ $reflection = new \ReflectionClass(Client::class);
+ $property = $reflection->getProperty('user_agent');
+ $property->setAccessible(true);
+
+ $client = new Client();
+ $userAgent = $property->getValue($client);
+
+ $this->assertStringContainsString('2.0.0', $userAgent);
+ $this->assertStringContainsString('DatingVIP-API', $userAgent);
+ }
+}
diff --git a/tests/CommandTest.php b/tests/CommandTest.php
new file mode 100644
index 0000000..8bc8d77
--- /dev/null
+++ b/tests/CommandTest.php
@@ -0,0 +1,131 @@
+assertSame('', $command->getName());
+ $this->assertSame([], $command->getData());
+ $this->assertFalse($command->isValid());
+ }
+
+ public function testConstructorWithName(): void
+ {
+ $command = new Command('controller.action');
+
+ $this->assertSame('controller.action', $command->getName());
+ $this->assertSame([], $command->getData());
+ $this->assertTrue($command->isValid());
+ }
+
+ public function testConstructorWithNameAndData(): void
+ {
+ $data = ['key' => 'value', 'foo' => 'bar'];
+ $command = new Command('controller.action', $data);
+
+ $this->assertSame('controller.action', $command->getName());
+ $this->assertSame($data, $command->getData());
+ $this->assertTrue($command->isValid());
+ }
+
+ public function testSetWithValidCommand(): void
+ {
+ $command = new Command();
+ $result = $command->set('test.method', ['param' => 'value']);
+
+ $this->assertTrue($result);
+ $this->assertSame('test.method', $command->getName());
+ $this->assertSame(['param' => 'value'], $command->getData());
+ }
+
+ public function testSetWithInvalidCommand(): void
+ {
+ $command = new Command();
+ $result = $command->set('invalid', []);
+
+ $this->assertFalse($result);
+ $this->assertFalse($command->isValid());
+ }
+
+ public function testIsValidWithDot(): void
+ {
+ $command = new Command('controller.action');
+ $this->assertTrue($command->isValid());
+ }
+
+ public function testIsValidWithMultipleDots(): void
+ {
+ $command = new Command('module.controller.action');
+ $this->assertTrue($command->isValid());
+ }
+
+ public function testIsValidWithoutDot(): void
+ {
+ $command = new Command('invalid');
+ $this->assertFalse($command->isValid());
+ }
+
+ public function testIsValidWithEmptyName(): void
+ {
+ $command = new Command('');
+ $this->assertFalse($command->isValid());
+ }
+
+ public function testReservedKeyFiltering(): void
+ {
+ $data = [
+ 'param1' => 'value1',
+ Command::VAR_CONTROLLER => 'should_be_removed',
+ 'param2' => 'value2'
+ ];
+
+ $command = new Command('test.method', $data);
+
+ $this->assertArrayNotHasKey(Command::VAR_CONTROLLER, $command->getData());
+ $this->assertArrayHasKey('param1', $command->getData());
+ $this->assertArrayHasKey('param2', $command->getData());
+ }
+
+ public function testGetName(): void
+ {
+ $command = new Command('controller.action');
+ $this->assertSame('controller.action', $command->getName());
+ }
+
+ public function testGetData(): void
+ {
+ $data = ['key' => 'value'];
+ $command = new Command('test.method', $data);
+ $this->assertSame($data, $command->getData());
+ }
+
+ public function testGetDataReturnsEmptyArray(): void
+ {
+ $command = new Command('test.method');
+ $this->assertSame([], $command->getData());
+ }
+
+ public function testSpecialCharactersInName(): void
+ {
+ $command = new Command('user.get-profile');
+ $this->assertTrue($command->isValid());
+ $this->assertSame('user.get-profile', $command->getName());
+ }
+
+ public function testSetUpdatesNameAndData(): void
+ {
+ $command = new Command('old.method', ['old' => 'data']);
+ $command->set('new.method', ['new' => 'data']);
+
+ $this->assertSame('new.method', $command->getName());
+ $this->assertSame(['new' => 'data'], $command->getData());
+ }
+}
diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php
new file mode 100644
index 0000000..aeb7e94
--- /dev/null
+++ b/tests/ResponseTest.php
@@ -0,0 +1,302 @@
+ ['id' => 1]]);
+ $response = new Response($data);
+
+ $this->assertSame($data, $response->getRawResponse());
+ $this->assertSame(['result' => ['id' => 1]], $response->getRawData());
+ }
+
+ public function testConstructorWithNvpFormat(): void
+ {
+ $data = 'key1=value1&key2=value2';
+ $response = new Response($data, 'nvp');
+
+ $this->assertSame($data, $response->getRawResponse());
+ $this->assertSame(['key1' => 'value1', 'key2' => 'value2'], $response->getRawData());
+ }
+
+ public function testConstructorWithEmptyResponse(): void
+ {
+ $response = new Response('');
+
+ $this->assertSame('', $response->getRawResponse());
+ $this->assertSame([], $response->getRawData());
+ }
+
+ public function testConstructorWithInvalidJson(): void
+ {
+ $response = new Response('{invalid json}');
+
+ $this->assertSame('{invalid json}', $response->getRawResponse());
+ $error = $response->getRawError();
+ $this->assertNotEmpty($error);
+ $this->assertArrayHasKey('error', $error);
+ $this->assertArrayHasKey('msg', $error);
+ }
+
+ public function testGetReturnsJsonString(): void
+ {
+ $data = json_encode(['test' => 'value']);
+ $response = new Response($data);
+
+ $result = $response->get();
+ $this->assertIsString($result);
+ $this->assertJson($result);
+ }
+
+ public function testGetReturnsNvpString(): void
+ {
+ $data = 'key=value&foo=bar';
+ $response = new Response($data, 'nvp');
+
+ $result = $response->get();
+ $this->assertIsString($result);
+ $this->assertStringContainsString('key=value', $result);
+ }
+
+ public function testSetWithValidJson(): void
+ {
+ $response = new Response();
+ $json = json_encode(['result' => 'success']);
+ $result = $response->set($json);
+
+ $this->assertTrue($result);
+ $this->assertSame(['result' => 'success'], $response->getRawData());
+ $this->assertEmpty($response->getRawError());
+ }
+
+ public function testSetWithInvalidJson(): void
+ {
+ $response = new Response();
+ $result = $response->set('{invalid}');
+
+ $this->assertFalse($result);
+ $error = $response->getRawError();
+ $this->assertNotEmpty($error);
+ $this->assertSame(JSON_ERROR_SYNTAX, $error['error']);
+ }
+
+ public function testSetWithNvp(): void
+ {
+ $response = new Response('', 'nvp');
+ $result = $response->set('param1=val1¶m2=val2');
+
+ $this->assertTrue($result);
+ $this->assertSame(['param1' => 'val1', 'param2' => 'val2'], $response->getRawData());
+ }
+
+ public function testSetResultAndGetResult(): void
+ {
+ $response = new Response();
+ $data = ['id' => 1, 'name' => 'test'];
+ $response->setResult($data);
+
+ $this->assertTrue($response->hasResult());
+ $this->assertSame($data, $response->getResult());
+ }
+
+ public function testGetResultReturnsNullWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertNull($response->getResult());
+ }
+
+ public function testSetTextsAndGetTexts(): void
+ {
+ $response = new Response();
+ $texts = ['en' => 'Hello', 'es' => 'Hola'];
+ $response->setTexts($texts);
+
+ $this->assertTrue($response->hasTexts());
+ $this->assertSame($texts, $response->getTexts());
+ }
+
+ public function testGetTextsReturnsNullWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertNull($response->getTexts());
+ }
+
+ public function testSetStatusAndGetStatus(): void
+ {
+ $response = new Response();
+ $response->setStatus('success');
+
+ $this->assertSame('success', $response->getStatus());
+ }
+
+ public function testSetErrorsAndGetErrors(): void
+ {
+ $response = new Response();
+ $errors = ['error1', 'error2'];
+ $response->setErrors($errors);
+
+ $this->assertTrue($response->hasErrors());
+ $this->assertSame($errors, $response->getErrors());
+ }
+
+ public function testHasErrorsReturnsFalseWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasErrors());
+ }
+
+ public function testSetMessagesAndGetMessages(): void
+ {
+ $response = new Response();
+ $messages = ['msg1', 'msg2'];
+ $response->setMessages($messages);
+
+ $this->assertTrue($response->hasMessages());
+ $this->assertSame($messages, $response->getMessages());
+ }
+
+ public function testSetWarningsAndGetWarnings(): void
+ {
+ $response = new Response();
+ $warnings = ['warning1'];
+ $response->setWarnings($warnings);
+
+ $this->assertTrue($response->hasWarnings());
+ $this->assertSame($warnings, $response->getWarnings());
+ }
+
+ public function testSetAnnouncementsAndGetAnnouncements(): void
+ {
+ $response = new Response();
+ $announcements = ['announcement1'];
+ $response->setAnnouncements($announcements);
+
+ $this->assertTrue($response->hasAnnouncements());
+ $this->assertSame($announcements, $response->getAnnouncements());
+ }
+
+ public function testSetAppErrorsAndGetAppErrors(): void
+ {
+ $response = new Response();
+ $appErrors = ['app_error1'];
+ $response->setAppErrors($appErrors);
+
+ $this->assertTrue($response->hasAppErrors());
+ $this->assertSame($appErrors, $response->getAppErrors());
+ }
+
+ public function testHasMessagesReturnsFalseWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasMessages());
+ }
+
+ public function testHasWarningsReturnsFalseWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasWarnings());
+ }
+
+ public function testHasAnnouncementsReturnsFalseWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasAnnouncements());
+ }
+
+ public function testHasAppErrorsReturnsFalseWhenNotSet(): void
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasAppErrors());
+ }
+
+ public function testGetRawResponse(): void
+ {
+ $rawData = '{"test":"value"}';
+ $response = new Response($rawData);
+ $this->assertSame($rawData, $response->getRawResponse());
+ }
+
+ public function testGetRawData(): void
+ {
+ $response = new Response('{"key":"value"}');
+ $this->assertSame(['key' => 'value'], $response->getRawData());
+ }
+
+ public function testGetRawErrorWhenNoError(): void
+ {
+ $response = new Response('{"valid":"json"}');
+ $this->assertEmpty($response->getRawError());
+ }
+
+ public function testNestedArraysInResult(): void
+ {
+ $data = ['result' => ['nested' => ['deep' => 'value']]];
+ $response = new Response(json_encode($data));
+
+ $this->assertSame($data['result'], $response->getResult());
+ }
+
+ public function testSpecialCharactersInData(): void
+ {
+ $data = ['text' => 'Hello "World" & Co.'];
+ $response = new Response(json_encode($data));
+
+ $this->assertSame($data, $response->getRawData());
+ }
+
+ public function testSetEmptyJsonString(): void
+ {
+ $response = new Response();
+ $result = $response->set('');
+
+ $this->assertTrue($result);
+ $this->assertSame([], $response->getRawData());
+ }
+
+ public function testMultipleMetaFields(): void
+ {
+ $response = new Response();
+ $response->setStatus('ok');
+ $response->setErrors(['err1']);
+ $response->setMessages(['msg1']);
+ $response->setWarnings(['warn1']);
+
+ $this->assertSame('ok', $response->getStatus());
+ $this->assertSame(['err1'], $response->getErrors());
+ $this->assertSame(['msg1'], $response->getMessages());
+ $this->assertSame(['warn1'], $response->getWarnings());
+ }
+
+ public function testCompleteResponseStructure(): void
+ {
+ $data = [
+ 'result' => ['id' => 1],
+ 'texts' => ['en' => 'Hello'],
+ 'meta' => [
+ 'status' => 'success',
+ 'errors' => [],
+ 'messages' => ['Operation completed'],
+ 'warnings' => [],
+ 'announcements' => ['New feature!'],
+ 'app' => ['debug' => 'info']
+ ]
+ ];
+
+ $response = new Response(json_encode($data));
+
+ $this->assertTrue($response->hasResult());
+ $this->assertTrue($response->hasTexts());
+ $this->assertSame(['id' => 1], $response->getResult());
+ $this->assertSame(['en' => 'Hello'], $response->getTexts());
+ $this->assertSame('success', $response->getStatus());
+ $this->assertSame(['Operation completed'], $response->getMessages());
+ $this->assertSame(['New feature!'], $response->getAnnouncements());
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..03c2589
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,11 @@
+