From 996404fcf861a59319745b3b0e8dd7df0adb7bfa Mon Sep 17 00:00:00 2001 From: "Abdulmhsen B. A. A" Date: Fri, 18 Feb 2022 17:26:27 +0300 Subject: [PATCH 1/5] fixed variable casing for profiler. --- src/Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command.php b/src/Command.php index 84eda038..e9bd1de1 100644 --- a/src/Command.php +++ b/src/Command.php @@ -46,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'PHP_VERSION' => PHP_VERSION, 'PHP_VERSION_ID' => PHP_VERSION_ID, 'PHP_OS' => PHP_OS, - 'DOCKER' => env('in_docker') ? 'Yes' : 'No', + 'DOCKER' => env('IN_DOCKER') ? 'Yes' : 'No', 'SYSTEM' => php_uname('s') . ' ' . php_uname('r') . ' ' . php_uname('v') . ' ' . php_uname('m'), 'DOCUMENT_ROOT' => env('IN_DOCKER') ? '/docker/' : '/cli', 'REMOTE_ADDR' => '127.0.0.1', From 3d8fdc20ac97bf4a1921897591a73bff02e06562 Mon Sep 17 00:00:00 2001 From: "Abdulmhsen B. A. A" Date: Sat, 19 Feb 2022 16:38:24 +0300 Subject: [PATCH 2/5] Removed usage of DS constant. --- config/config.php | 8 ++++---- config/config.yaml | 2 +- console | 22 ++++------------------ pre_init.php | 15 +++++++++++++++ public/index.php | 16 +--------------- src/Commands/Config/DumpCommand.php | 9 ++++----- src/Commands/Config/GenerateCommand.php | 2 +- src/Commands/State/ExportCommand.php | 2 +- src/Commands/State/ImportCommand.php | 2 +- src/Libs/KernelConsole.php | 4 ++-- src/Libs/Storage/PDO/PDOMigrations.php | 10 +++++----- src/Libs/helpers.php | 4 ++-- 12 files changed, 41 insertions(+), 55 deletions(-) create mode 100644 pre_init.php diff --git a/config/config.php b/config/config.php index 256ce5c1..47e94afd 100644 --- a/config/config.php +++ b/config/config.php @@ -17,7 +17,7 @@ 'version' => 'v0.0.10-alpha', 'tz' => null, 'path' => fixPath( - env('WS_DATA_PATH', fn() => env('IN_DOCKER') ? '/config' : realpath(__DIR__ . DS . '..' . DS . 'var')) + env('WS_DATA_PATH', fn() => env('IN_DOCKER') ? '/config' : realpath(__DIR__ . '/../var')) ), ]; @@ -26,7 +26,7 @@ 'opts' => [ 'dsn' => env( 'WS_STORAGE_PDO_DSN', - fn() => 'sqlite:' . ag($config, 'path') . DS . 'db' . DS . 'watchstate.db' + fn() => 'sqlite:' . ag($config, 'path') . '/db/watchstate.db' ), 'username' => null, 'password' => null, @@ -76,7 +76,7 @@ 'options' => [ 'save.handler' => 'file', 'save.handler.file' => [ - 'filename' => ag($config, 'path') . DS . 'logs' . DS . 'profiler_' . gmdate('Y_m_d_His') . '.json' + 'filename' => ag($config, 'path') . '/logs/profiler_' . gmdate('Y_m_d_His') . '.json' ], ], ], @@ -93,7 +93,7 @@ 'type' => 'stream', 'enabled' => env('WS_LOGGER_FILE_ENABLE', false), 'level' => env('WS_LOGGER_FILE_LEVEL', Logger::ERROR), - 'filename' => env('WS_LOGGER_FILE', fn() => ag($config, 'path') . DS . 'logs' . DS . 'app.log'), + 'filename' => env('WS_LOGGER_FILE', fn() => ag($config, 'path') . '/logs/app.log'), ], 'syslog' => [ 'type' => 'syslog', diff --git a/config/config.yaml b/config/config.yaml index 496d90b2..3dfd591e 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -6,7 +6,7 @@ logger: enabled: true type: stream level: ERROR - filename: '%(path)%(DS)logs%(DS)app.log' + filename: '%(path)%/logs/app.log' syslog: enabled: false type: syslog diff --git a/console b/console index 9ee0d081..7615e1a1 100644 --- a/console +++ b/console @@ -10,21 +10,7 @@ if (function_exists('posix_geteuid') && 0 === posix_geteuid()) { error_reporting(E_ALL); ini_set('display_errors', 'On'); -if (!defined('BASE_MEMORY')) { - define('BASE_MEMORY', memory_get_usage()); -} - -if (!defined('BASE_PEAK_MEMORY')) { - define('BASE_PEAK_MEMORY', memory_get_peak_usage()); -} - -if (!defined('DS')) { - define('DS', DIRECTORY_SEPARATOR); -} - -if (!defined('ROOT_PATH')) { - define('ROOT_PATH', realpath(__DIR__)); -} +require __DIR__ . '/pre_init.php'; set_error_handler(function (int $number, mixed $error, mixed $file, int $line) { $errno = $number & error_reporting(); @@ -68,13 +54,13 @@ set_exception_handler(function (Throwable $e) { exit(1); }); -if (!file_exists(__DIR__ . DS . 'vendor' . DS . 'autoload.php')) { +if (!file_exists(__DIR__ . '/vendor/autoload.php')) { fwrite(STDERR, 'Composer dependencies are missing. Run the following commands.' . PHP_EOL); - fwrite(STDERR, sprintf('cd %s', dirname(__DIR__)) . PHP_EOL); + fwrite(STDERR, sprintf('cd %s', __DIR__) . PHP_EOL); fwrite(STDERR, 'composer install --optimize-autoloader' . PHP_EOL); exit(1); } -require __DIR__ . DS . 'vendor' . DS . 'autoload.php'; +require __DIR__ . '/vendor/autoload.php'; (new App\Libs\KernelConsole())->boot()->runConsole(); diff --git a/pre_init.php b/pre_init.php new file mode 100644 index 00000000..ec7e8c69 --- /dev/null +++ b/pre_init.php @@ -0,0 +1,15 @@ + 'config' . DS . 'servers.yaml', - 'config' => 'config' . DS . 'config.yaml', + 'servers' => 'config/servers.yaml', + 'config' => 'config/config.yaml', ]; protected function configure(): void @@ -51,7 +51,7 @@ protected function runCommand(InputInterface $input, OutputInterface $output): i throw new RuntimeException(sprintf('Unable to write to location path. \'%s\'.', $path)); } - $file = $path . DS . self::$configs[$type]; + $file = $path . '/' . self::$configs[$type]; if (file_exists($file) && !$input->getOption('override')) { $message = sprintf('File exists at \'%s\'. use [-w, --override] flag to overwrite the file.', $file); @@ -60,7 +60,6 @@ protected function runCommand(InputInterface $input, OutputInterface $output): i } $kvSore = [ - 'DS' => DS, 'path' => Config::get('path'), ]; @@ -69,7 +68,7 @@ protected function runCommand(InputInterface $input, OutputInterface $output): i str_replace( array_map(fn($n) => '%(' . $n . ')', array_keys($kvSore)), array_values($kvSore), - file_get_contents(ROOT_PATH . DS . self::$configs[$type]) + file_get_contents(ROOT_PATH . '/' . self::$configs[$type]) ) ); diff --git a/src/Commands/Config/GenerateCommand.php b/src/Commands/Config/GenerateCommand.php index 5eb3a57b..93e3b5be 100644 --- a/src/Commands/Config/GenerateCommand.php +++ b/src/Commands/Config/GenerateCommand.php @@ -27,7 +27,7 @@ protected function configure(): void protected function runCommand(InputInterface $input, OutputInterface $output): int { $yaml = []; - $config = Config::get('path') . DS . 'config' . DS . 'config.yaml'; + $config = Config::get('path') . '/config/config.yaml'; if (file_exists($config)) { diff --git a/src/Commands/State/ExportCommand.php b/src/Commands/State/ExportCommand.php index 07d7abbb..19f8a5a1 100644 --- a/src/Commands/State/ExportCommand.php +++ b/src/Commands/State/ExportCommand.php @@ -258,7 +258,7 @@ protected function runCommand(InputInterface $input, OutputInterface $output): i // -- Update Server.yaml with new lastSync date. file_put_contents( - $newConfig ?? Config::get('path') . DS . 'config' . DS . 'servers.yaml', + $newConfig ?? Config::get('path') . '/config/servers.yaml', Yaml::dump(Config::get('servers', []), 8, 2) ); diff --git a/src/Commands/State/ImportCommand.php b/src/Commands/State/ImportCommand.php index bf682dd8..117b36e3 100644 --- a/src/Commands/State/ImportCommand.php +++ b/src/Commands/State/ImportCommand.php @@ -266,7 +266,7 @@ protected function runCommand(InputInterface $input, OutputInterface $output): i // -- Update Server.yaml with new lastSync date. file_put_contents( - $newConfig ?? Config::get('path') . DS . 'config' . DS . 'servers.yaml', + $newConfig ?? Config::get('path') . '/config/servers.yaml', Yaml::dump(Config::get('servers', []), 8, 2) ); diff --git a/src/Libs/KernelConsole.php b/src/Libs/KernelConsole.php index 3f4a3843..0412e269 100644 --- a/src/Libs/KernelConsole.php +++ b/src/Libs/KernelConsole.php @@ -59,14 +59,14 @@ public function boot(): self // -- load user config. (function () { - $path = Config::get('path') . DS . 'config' . DS . 'config.yaml'; + $path = Config::get('path') . '/config/config.yaml'; if (file_exists($path)) { Config::init(function () use ($path) { return array_replace_recursive(Config::getAll(), Yaml::parseFile($path)); }); } - $path = Config::get('path') . DS . 'config' . DS . 'servers.yaml'; + $path = Config::get('path') . '/config/servers.yaml'; if (file_exists($path)) { Config::save('servers', Yaml::parseFile($path)); } diff --git a/src/Libs/Storage/PDO/PDOMigrations.php b/src/Libs/Storage/PDO/PDOMigrations.php index e55cbff7..e479845f 100644 --- a/src/Libs/Storage/PDO/PDOMigrations.php +++ b/src/Libs/Storage/PDO/PDOMigrations.php @@ -21,8 +21,8 @@ final class PDOMigrations public function __construct(private PDO $pdo) { - $this->path = __DIR__ . DS . 'Migrations'; - $this->versionFile = Config::get('path') . DS . 'db' . DS . 'pdo_migrations_version'; + $this->path = __DIR__ . '/Migrations'; + $this->versionFile = Config::get('path') . '/db/pdo_migrations_version'; $this->driver = $this->getDriver(); } @@ -104,10 +104,10 @@ public function make(string $name, OutputInterface $output): string $fileName = sprintf('%s_%d_%s.sql', $this->driver, time(), $name); - $file = $this->path . DS . $fileName; + $file = $this->path . '/' . $fileName; if (!touch($file)) { - throw new RuntimeException(sprintf('Unable to create new migration at \'%s\'.', $this->path . DS)); + throw new RuntimeException(sprintf('Unable to create new migration at \'%s\'.', $this->path)); } file_put_contents( @@ -171,7 +171,7 @@ private function parseFiles(): array { $migrations = []; - foreach ((array)glob($this->path . DS . '*.sql') as $file) { + foreach ((array)glob($this->path . '/*.sql') as $file) { if (!is_string($file) || false === ($f = realpath($file))) { throw new RuntimeException(sprintf('Unable to get real path to \'%s\'', $file)); } diff --git a/src/Libs/helpers.php b/src/Libs/helpers.php index b02c859a..ef19502a 100644 --- a/src/Libs/helpers.php +++ b/src/Libs/helpers.php @@ -204,7 +204,7 @@ function ag_delete(array $array, string|int $path, string $separator = '.'): arr if (!function_exists('fixPath')) { function fixPath(string $path): string { - return rtrim(implode(DS, explode(DS, $path)), DS); + return rtrim(implode(DIRECTORY_SEPARATOR, explode(DIRECTORY_SEPARATOR, $path)), DIRECTORY_SEPARATOR); } } @@ -231,7 +231,7 @@ function saveWebhookPayload(ServerRequestInterface $request, string $name, array ]; @file_put_contents( - Config::get('path') . DS . 'logs' . DS . sprintf('webhook.%s.json', $name), + Config::get('path') . '/logs/' . sprintf('webhook.%s.json', $name), json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) ); } From 8dd39b7e6e03b18e8c5b3600386c91720b6049b8 Mon Sep 17 00:00:00 2001 From: "Abdulmhsen B. A. A" Date: Sat, 19 Feb 2022 20:56:51 +0300 Subject: [PATCH 3/5] As the API stabilizing, we started adding tests. --- .dockerignore | 1 + .gitignore | 1 + composer.json | 11 +- composer.lock | 2277 +++++++++++++++++++++++-- phpunit.xml.dist | 11 + public/index.php | 90 +- src/Libs/Entity/StateEntity.php | 6 + src/Libs/Entity/StateInterface.php | 1 + src/Libs/Storage/PDO/PDOAdapter.php | 413 ++--- src/Libs/Storage/StorageException.php | 12 + src/Libs/Storage/StorageInterface.php | 63 +- src/Libs/helpers.php | 91 +- tests/Storage/PDOAdapterTest.php | 313 ++++ tests/bootstrap.php | 15 + 14 files changed, 2785 insertions(+), 520 deletions(-) create mode 100644 phpunit.xml.dist create mode 100644 src/Libs/Storage/StorageException.php create mode 100644 tests/Storage/PDOAdapterTest.php create mode 100644 tests/bootstrap.php diff --git a/.dockerignore b/.dockerignore index e9d9b9c9..dde3d033 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,4 @@ !./docker/config/.gitignore ./var/* !./var/.gitignore +.phpunit.result.cache diff --git a/.gitignore b/.gitignore index 1eaa3875..db782c29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea/* /vendor/* +.phpunit.result.cache diff --git a/composer.json b/composer.json index 5cacc2a2..9a4faff4 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,9 @@ "*": "dist" } }, + "scripts": { + "test": "vendor/bin/phpunit --colors=always" + }, "require": { "php": ">=8.1", "ext-pdo": "*", @@ -33,7 +36,13 @@ "require-dev": { "roave/security-advisories": "dev-latest", "symfony/var-dumper": "^6.0", - "perftools/php-profiler": "^1.0" + "perftools/php-profiler": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests" + } }, "autoload": { "files": [ diff --git a/composer.lock b/composer.lock index 01064b95..3e7db2bc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2154f627531b8a6fc293d6d2b3cba1a1", + "content-hash": "661be76a15d00ad6df2adbe8d3b96059", "packages": [ { "name": "dragonmantank/cron-expression", @@ -1836,6 +1836,186 @@ } ], "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.13.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + }, + "time": "2021-11-30T19:35:32+00:00" + }, { "name": "perftools/php-profiler", "version": "1.0.0", @@ -1899,157 +2079,916 @@ "time": "2021-10-18T01:40:28+00:00" }, { - "name": "roave/security-advisories", - "version": "dev-latest", + "name": "phar-io/manifest", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "6ba1494c9aaa556dc45e13eaab644a280ad76558" + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/6ba1494c9aaa556dc45e13eaab644a280ad76558", - "reference": "6ba1494c9aaa556dc45e13eaab644a280ad76558", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, - "conflict": { - "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", - "akaunting/akaunting": "<2.1.13", - "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", - "amazing/media2click": ">=1,<1.3.3", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<1.0.1", - "amphp/http-client": ">=4,<4.4", - "anchorcms/anchor-cms": "<=0.12.7", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", - "area17/twill": "<1.2.5|>=2,<2.5.3", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bagisto/bagisto": "<0.1.5", - "barrelstrength/sprout-base-email": "<1.2.7", - "barrelstrength/sprout-forms": "<3.9", - "baserproject/basercms": "<4.5.4", - "billz/raspap-webgui": "<=2.6.6", - "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", - "bolt/bolt": "<3.7.2", - "bolt/core": "<4.1.13", - "bottelet/flarepoint": "<2.2.1", - "brightlocal/phpwhois": "<=4.2.5", - "buddypress/buddypress": "<7.2.1", - "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "bytefury/crater": "<6.0.2", - "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": "<4.0.6", - "cardgate/magento2": "<2.0.33", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", - "catfan/medoo": "<1.7.5", - "centreon/centreon": "<20.10.7", - "cesnet/simplesamlphp-module-proxystatistics": "<3.1", - "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.1.8", - "codiad/codiad": "<=2.8.4", - "composer/composer": "<1.10.23|>=2-alpha.1,<2.1.9", - "concrete5/concrete5": "<9", - "concrete5/core": "<8.5.7", - "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.39", - "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|= 4.10.0", - "contao/listing-bundle": ">=4,<4.4.8", - "craftcms/cms": "<3.7.14", - "croogo/croogo": "<3.0.7", - "datadog/dd-trace": ">=0.30,<0.30.2", - "david-garcia/phpwhois": "<=4.3.1", - "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", - "directmailteam/direct-mail": "<5.2.4", - "doctrine/annotations": ">=1,<1.2.7", - "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", - "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<=14.0.5|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", - "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", - "dweeves/magmi": "<=0.7.24", - "ecodev/newsletter": "<=4", - "elgg/elgg": "<3.3.24|>=4,<4.0.5", - "endroid/qr-code-bundle": "<3.4.2", - "enshrined/svg-sanitize": "<0.15", - "erusev/parsedown": "<1.7.2", - "ether/logs": "<3.0.4", - "ezsystems/demobundle": ">=5.4,<5.4.6.1", - "ezsystems/ez-support-tools": ">=2.2,<2.2.3", - "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", - "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", - "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<=1.5.25", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", - "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", - "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", - "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.26", - "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", - "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", - "ezsystems/repository-forms": ">=2.3,<2.3.2.1", - "ezyang/htmlpurifier": "<4.1.1", - "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "feehi/cms": "<=2.1.1", - "feehi/feehicms": "<=0.1.3", - "firebase/php-jwt": "<2", - "flarum/core": ">=1,<=1.0.1", - "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", - "flarum/tags": "<=0.1-beta.13", - "fluidtypo3/vhs": "<5.1.1", - "fooman/tcpdf": "<6.2.22", - "forkcms/forkcms": "<=5.9.2", - "fossar/tcpdf-parser": "<6.2.22", - "francoisjacquet/rosariosis": "<8.1.1", - "friendsofsymfony/oauth2-php": "<1.3", - "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", - "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", - "froala/wysiwyg-editor": "<3.2.7", - "fuel/core": "<1.8.1", - "gaoming13/wechat-php-sdk": "<=1.10.2", - "getgrav/grav": "<1.7.28", - "getkirby/cms": "<3.5.8", - "getkirby/panel": "<2.5.14", - "gilacms/gila": "<=1.11.4", - "globalpayments/php-sdk": "<2", - "google/protobuf": "<3.15", - "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", - "gree/jose": "<=2.2", - "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.6.5", - "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", - "helloxz/imgurl": "<=2.31", - "hillelcoren/invoice-ninja": "<5.3.35", - "hjue/justwriting": "<=1", - "hov/jobfair": "<1.0.13|>=2,<2.0.2", - "ibexa/post-install": "<=1.0.4", - "icecoder/icecoder": "<=8.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", - "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", - "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", - "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", - "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "impresscms/impresscms": "<=1.4.2", - "in2code/femanager": "<5.5.1|>=6,<6.3.1", - "intelliants/subrion": "<=4.2.1", - "ivankristianto/phpwhois": "<=4.3", - "jackalope/jackalope-doctrine-dbal": "<1.7.4", - "james-heinrich/getid3": "<1.9.21", - "joomla/archive": "<1.1.10", - "joomla/session": "<1.3.1", - "jsdecena/laracom": "<2.0.9", + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "15a90844ad40f127afd244c0cad228de2a80052a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/15a90844ad40f127afd244c0cad228de2a80052a", + "reference": "15a90844ad40f127afd244c0cad228de2a80052a", + "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.1.1" + }, + "time": "2022-02-07T21:56:48+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + }, + "time": "2022-01-04T19:58:01+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/665a1ac0a763c51afc30d6d130dac0813092b17f", + "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.11" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-18T12:46:09+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.14", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "1883687169c017d6ae37c58883ca3994cfc34189" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1883687169c017d6ae37c58883ca3994cfc34189", + "reference": "1883687169c017d6ae37c58883ca3994cfc34189", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.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", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.14" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-18T12:54:07+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-latest", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "6ba1494c9aaa556dc45e13eaab644a280ad76558" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/6ba1494c9aaa556dc45e13eaab644a280ad76558", + "reference": "6ba1494c9aaa556dc45e13eaab644a280ad76558", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", + "akaunting/akaunting": "<2.1.13", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", + "amphp/http-client": ">=4,<4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", + "aws/aws-sdk-php": ">=3,<3.2.1", + "bagisto/bagisto": "<0.1.5", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "baserproject/basercms": "<4.5.4", + "billz/raspap-webgui": "<=2.6.6", + "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "bolt/bolt": "<3.7.2", + "bolt/core": "<4.1.13", + "bottelet/flarepoint": "<2.2.1", + "brightlocal/phpwhois": "<=4.2.5", + "buddypress/buddypress": "<7.2.1", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bytefury/crater": "<6.0.2", + "cachethq/cachet": "<2.5.1", + "cakephp/cakephp": "<4.0.6", + "cardgate/magento2": "<2.0.33", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cartalyst/sentry": "<=2.1.6", + "catfan/medoo": "<1.7.5", + "centreon/centreon": "<20.10.7", + "cesnet/simplesamlphp-module-proxystatistics": "<3.1", + "codeception/codeception": "<3.1.3|>=4,<4.1.22", + "codeigniter/framework": "<=3.0.6", + "codeigniter4/framework": "<4.1.8", + "codiad/codiad": "<=2.8.4", + "composer/composer": "<1.10.23|>=2-alpha.1,<2.1.9", + "concrete5/concrete5": "<9", + "concrete5/core": "<8.5.7", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/core": ">=2,<3.5.39", + "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|= 4.10.0", + "contao/listing-bundle": ">=4,<4.4.8", + "craftcms/cms": "<3.7.14", + "croogo/croogo": "<3.0.7", + "datadog/dd-trace": ">=0.30,<0.30.2", + "david-garcia/phpwhois": "<=4.3.1", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", + "directmailteam/direct-mail": "<5.2.4", + "doctrine/annotations": ">=1,<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<=0.7.1", + "doctrine/mongodb-odm": ">=1,<1.0.2", + "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", + "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<=14.0.5|>= 3.3.beta1, < 13.0.2", + "dompdf/dompdf": ">=0.6,<0.6.2", + "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "dweeves/magmi": "<=0.7.24", + "ecodev/newsletter": "<=4", + "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "endroid/qr-code-bundle": "<3.4.2", + "enshrined/svg-sanitize": "<0.15", + "erusev/parsedown": "<1.7.2", + "ether/logs": "<3.0.4", + "ezsystems/demobundle": ">=5.4,<5.4.6.1", + "ezsystems/ez-support-tools": ">=2.2,<2.2.3", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", + "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<=1.5.25", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", + "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", + "ezsystems/ezplatform-user": ">=1,<1.0.1", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.26", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1", + "ezyang/htmlpurifier": "<4.1.1", + "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "feehi/cms": "<=2.1.1", + "feehi/feehicms": "<=0.1.3", + "firebase/php-jwt": "<2", + "flarum/core": ">=1,<=1.0.1", + "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", + "flarum/tags": "<=0.1-beta.13", + "fluidtypo3/vhs": "<5.1.1", + "fooman/tcpdf": "<6.2.22", + "forkcms/forkcms": "<=5.9.2", + "fossar/tcpdf-parser": "<6.2.22", + "francoisjacquet/rosariosis": "<8.1.1", + "friendsofsymfony/oauth2-php": "<1.3", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", + "froala/wysiwyg-editor": "<3.2.7", + "fuel/core": "<1.8.1", + "gaoming13/wechat-php-sdk": "<=1.10.2", + "getgrav/grav": "<1.7.28", + "getkirby/cms": "<3.5.8", + "getkirby/panel": "<2.5.14", + "gilacms/gila": "<=1.11.4", + "globalpayments/php-sdk": "<2", + "google/protobuf": "<3.15", + "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gree/jose": "<=2.2", + "gregwar/rst": "<1.0.3", + "grumpydictator/firefly-iii": "<5.6.5", + "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", + "helloxz/imgurl": "<=2.31", + "hillelcoren/invoice-ninja": "<5.3.35", + "hjue/justwriting": "<=1", + "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "ibexa/post-install": "<=1.0.4", + "icecoder/icecoder": "<=8.1", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", + "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "impresscms/impresscms": "<=1.4.2", + "in2code/femanager": "<5.5.1|>=6,<6.3.1", + "intelliants/subrion": "<=4.2.1", + "ivankristianto/phpwhois": "<=4.3", + "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "james-heinrich/getid3": "<1.9.21", + "joomla/archive": "<1.1.10", + "joomla/session": "<1.3.1", + "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", "kevinpapst/kimai2": "<1.16.7", @@ -2314,40 +3253,1004 @@ "zfr/zfr-oauth2-server-module": "<0.1.2", "zoujingli/thinkadmin": "<6.0.22" }, - "default-branch": true, - "type": "metapackage", + "default-branch": true, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2022-02-17T14:18:41+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T14:18:36+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ { - "name": "Ilya Tribusean", - "email": "slash3b@gmail.com", - "role": "maintainer" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/Roave/SecurityAdvisories/issues", - "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { - "url": "https://github.com/Ocramius", + "url": "https://github.com/sebastianbergmann", "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", - "type": "tidelift" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "time": "2022-02-17T14:18:41+00:00" + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-06-15T12:49:02+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "symfony/var-dumper", @@ -2436,6 +4339,56 @@ } ], "time": "2022-01-17T16:30:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" } ], "aliases": [], diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..38c3f198 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,11 @@ + + + + + tests + + + + diff --git a/public/index.php b/public/index.php index 42160982..47667bad 100644 --- a/public/index.php +++ b/public/index.php @@ -2,15 +2,7 @@ declare(strict_types=1); -use App\Libs\Config; -use App\Libs\Container; -use App\Libs\HttpException; -use App\Libs\Servers\ServerInterface; -use App\Libs\Storage\StorageInterface; -use Nyholm\Psr7\Response; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Psr\Log\LoggerInterface; error_reporting(E_ALL); ini_set('error_reporting', 'On'); @@ -41,82 +33,8 @@ require __DIR__ . '/../vendor/autoload.php'; -$fn = function (ServerRequestInterface $request): ResponseInterface { - try { - if (true !== Config::get('webhook.enabled', false)) { - throw new HttpException('Webhook is disabled via config.', 500); - } - - if (null === Config::get('webhook.apikey', null)) { - throw new HttpException('No webhook.apikey is set in config.', 500); - } - - // -- get apikey from header or query. - $apikey = $request->getHeaderLine('x-apikey'); - if (empty($apikey)) { - $apikey = ag($request->getQueryParams(), 'apikey', ''); - if (empty($apikey)) { - throw new HttpException('No API key was given.', 400); - } - } - - if (!hash_equals(Config::get('webhook.apikey'), $apikey)) { - throw new HttpException('Invalid API key was given.', 401); - } - - if (null === ($type = ag($request->getQueryParams(), 'type', null))) { - throw new HttpException('No type was given via type= query.', 400); - } - - $types = Config::get('supported', []); - - if (null === ($backend = ag($types, $type))) { - throw new HttpException('Invalid server type was given.', 400); - } - - $class = new ReflectionClass($backend); - - if (!$class->implementsInterface(ServerInterface::class)) { - throw new HttpException('Invalid Parser Class.', 500); - } - - /** @var ServerInterface $backend */ - $entity = $backend::parseWebhook($request); - - if (null === $entity || !$entity->hasGuids()) { - return new Response(status: 200, headers: ['X-Status' => 'No GUIDs.']); - } - - $storage = Container::get(StorageInterface::class); - - if (null === ($backend = $storage->get($entity))) { - $storage->insert($entity); - return jsonResponse(status: 200, body: $entity->getAll()); - } - - if ($backend->updated > $entity->updated) { - return new Response( - status: 200, - headers: ['X-Status' => 'Entity date is older than what available in storage.'] - ); - } - - if ($backend->apply($entity)->isChanged()) { - $backend = $storage->update($backend); - - return jsonResponse(status: 200, body: $backend->getAll()); - } - - return new Response(status: 200, headers: ['X-Status' => 'Entity is unchanged.']); - } catch (HttpException $e) { - Container::get(LoggerInterface::class)->error($e->getMessage()); - - if (200 === $e->getCode()) { - return new Response(status: $e->getCode(), headers: ['X-Status' => $e->getMessage()]); - } - - return jsonResponse(status: $e->getCode(), body: ['error' => true, 'message' => $e->getMessage()]); +(new App\Libs\KernelConsole())->boot()->runHttp( + function (ServerRequestInterface $request) { + return serveHttpRequest($request); } -}; - -(new App\Libs\KernelConsole())->boot()->runHttp($fn); +); diff --git a/src/Libs/Entity/StateEntity.php b/src/Libs/Entity/StateEntity.php index 5f07c742..db2041fd 100644 --- a/src/Libs/Entity/StateEntity.php +++ b/src/Libs/Entity/StateEntity.php @@ -136,6 +136,12 @@ public function apply(StateInterface $entity, bool $guidOnly = false): self return $this; } + public function updateOriginal(): StateInterface + { + $this->data = $this->getAll(); + return $this; + } + private function isEqual(StateInterface $entity): bool { foreach ($this->getAll() as $key => $val) { diff --git a/src/Libs/Entity/StateInterface.php b/src/Libs/Entity/StateInterface.php index f1b16b7b..72799674 100644 --- a/src/Libs/Entity/StateInterface.php +++ b/src/Libs/Entity/StateInterface.php @@ -81,4 +81,5 @@ public function hasGuids(): bool; */ public function apply(StateInterface $entity, bool $guidOnly = false): StateInterface; + public function updateOriginal(): StateInterface; } diff --git a/src/Libs/Storage/PDO/PDOAdapter.php b/src/Libs/Storage/PDO/PDOAdapter.php index 94def5ed..241b81e9 100644 --- a/src/Libs/Storage/PDO/PDOAdapter.php +++ b/src/Libs/Storage/PDO/PDOAdapter.php @@ -6,6 +6,7 @@ use App\Libs\Container; use App\Libs\Entity\StateInterface; +use App\Libs\Storage\StorageException; use App\Libs\Storage\StorageInterface; use Closure; use DateTimeInterface; @@ -14,7 +15,6 @@ use PDOException; use PDOStatement; use Psr\Log\LoggerInterface; -use RuntimeException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -28,38 +28,26 @@ final class PDOAdapter implements StorageInterface ]; private PDO|null $pdo = null; - private string|null $driver = null; private bool $viaCommit = false; - private PDOStatement|null $stmtInsert = null; - private PDOStatement|null $stmtUpdate = null; - private PDOStatement|null $stmtDelete = null; + /** + * Cache Prepared Statements. + * + * @var array + */ + private array $stmt = [ + 'insert' => null, + 'update' => null, + ]; public function __construct(private LoggerInterface $logger) { } - public function getAll(DateTimeInterface|null $date = null): array - { - $arr = []; - - $sql = "SELECT * FROM state"; - - if (null !== $date) { - $sql .= ' WHERE updated > ' . $date->getTimestamp(); - } - - foreach ($this->pdo->query($sql) as $row) { - $arr[] = Container::get(StateInterface::class)::fromArray($row); - } - - return $arr; - } - public function setUp(array $opts): StorageInterface { if (null === ($opts['dsn'] ?? null)) { - throw new RuntimeException('No storage.opts.dsn (Data Source Name) was provided.'); + throw new StorageException('No storage.opts.dsn (Data Source Name) was provided.', 10); } $this->pdo = new PDO( @@ -75,13 +63,13 @@ public function setUp(array $opts): StorageInterface ) ); - $this->driver = $this->getDriver(); + $driver = $this->getDriver(); - if (!in_array($this->driver, $this->supported)) { - throw new RuntimeException(sprintf('%s Driver is not supported.', $this->driver)); + if (!in_array($driver, $this->supported)) { + throw new StorageException(sprintf('%s Driver is not supported.', $driver), 11); } - if (null !== ($exec = ag($opts, "exec.{$this->driver}")) && is_array($exec)) { + if (null !== ($exec = ag($opts, "exec.{$driver}")) && is_array($exec)) { foreach ($exec as $cmd) { $this->pdo->exec($cmd); } @@ -90,17 +78,10 @@ public function setUp(array $opts): StorageInterface return $this; } - public function setLogger(LoggerInterface $logger): StorageInterface - { - $this->logger = $logger; - - return $this; - } - public function insert(StateInterface $entity): StateInterface { if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); } try { @@ -111,24 +92,24 @@ public function insert(StateInterface $entity): StateInterface } if (null !== $data['id']) { - throw new RuntimeException( - sprintf('Trying to insert already saved entity #%s', $data['id']) + throw new StorageException( + sprintf('Trying to insert already saved entity #%s', $data['id']), 21 ); } unset($data['id']); - if (null === $this->stmtInsert) { - $this->stmtInsert = $this->pdo->prepare( - $this->pdoInsert('state', array_keys($data)) + if (null === ($this->stmt['insert'] ?? null)) { + $this->stmt['insert'] = $this->pdo->prepare( + $this->pdoInsert('state', StateInterface::ENTITY_KEYS) ); } - $this->stmtInsert->execute($data); + $this->stmt['insert']->execute($data); $entity->id = (int)$this->pdo->lastInsertId(); } catch (PDOException $e) { - $this->stmtInsert = null; + $this->stmt['insert'] = null; if (false === $this->viaCommit) { $this->logger->error($e->getMessage(), $entity->meta ?? []); return $entity; @@ -136,13 +117,56 @@ public function insert(StateInterface $entity): StateInterface throw $e; } - return $entity; + return $entity->updateOriginal(); + } + + public function get(StateInterface $entity): StateInterface|null + { + if (null === $this->pdo) { + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); + } + + $arr = array_intersect_key( + $entity->getAll(), + array_flip(StateInterface::ENTITY_GUIDS) + ); + + if (null !== $entity->id) { + $arr['id'] = $entity->id; + } + + return $this->matchAnyId($arr, $entity); + } + + public function getAll(DateTimeInterface|null $date = null, StateInterface|null $class = null): array + { + if (null === $this->pdo) { + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); + } + + $arr = []; + + $sql = 'SELECT * FROM state'; + + if (null !== $date) { + $sql .= ' WHERE updated > ' . $date->getTimestamp(); + } + + if (null === $class) { + $class = Container::get(StateInterface::class); + } + + foreach ($this->pdo->query($sql) as $row) { + $arr[] = $class::fromArray($row); + } + + return $arr; } public function update(StateInterface $entity): StateInterface { if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); } try { @@ -153,16 +177,18 @@ public function update(StateInterface $entity): StateInterface } if (null === $data['id']) { - throw new RuntimeException('Trying to update unsaved entity'); + throw new StorageException('Trying to update unsaved entity', 51); } - if (null === $this->stmtUpdate) { - $this->stmtUpdate = $this->pdo->prepare($this->pdoUpdate('state', array_keys($data))); + if (null === ($this->stmt['update'] ?? null)) { + $this->stmt['update'] = $this->pdo->prepare( + $this->pdoUpdate('state', StateInterface::ENTITY_KEYS) + ); } - $this->stmtUpdate->execute($data); + $this->stmt['update']->execute($data); } catch (PDOException $e) { - $this->stmtUpdate = null; + $this->stmt['update'] = null; if (false === $this->viaCommit) { $this->logger->error($e->getMessage(), $entity->meta ?? []); return $entity; @@ -170,31 +196,35 @@ public function update(StateInterface $entity): StateInterface throw $e; } - return $entity; + return $entity->updateOriginal(); } - public function get(StateInterface $entity): StateInterface|null + public function matchAnyId(array $ids, StateInterface|null $class = null): StateInterface|null { if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); } - if (null !== $entity->id) { - $stmt = $this->pdo->query("SELECT * FROM state WHERE id = " . (int)$entity->id); + if (null === $class) { + $class = Container::get(StateInterface::class); + } + + if (null !== ($ids['id'] ?? null)) { + $stmt = $this->pdo->query("SELECT * FROM state WHERE id = " . (int)$ids['id']); if (false === ($row = $stmt->fetch(PDO::FETCH_ASSOC))) { return null; } - return Container::get(StateInterface::class)::fromArray($row); + return $class::fromArray($row); } $cond = $where = []; foreach (StateInterface::ENTITY_GUIDS as $key) { - if (null === $entity->{$key}) { + if (null === ($ids[$key] ?? null)) { continue; } - $cond[$key] = $entity->{$key}; + $cond[$key] = $ids[$key]; } if (empty($cond)) { @@ -202,106 +232,47 @@ public function get(StateInterface $entity): StateInterface|null } foreach ($cond as $key => $_) { - $where[] = $this->escapeIdentifier($key) . ' = :' . $key; + $where[] = $key . ' = :' . $key; } $sqlWhere = implode(' OR ', $where); - $stmt = $this->pdo->prepare( - sprintf( - "SELECT * FROM %s WHERE %s LIMIT 1", - $this->escapeIdentifier('state'), - $sqlWhere - ) - ); + $cachedKey = md5($sqlWhere); - if (false === $stmt->execute($cond)) { - throw new RuntimeException('Unable to prepare sql statement'); - } - - if (false === ($row = $stmt->fetch(PDO::FETCH_ASSOC))) { - return null; - } - - return Container::get(StateInterface::class)::fromArray($row); - } - - public function matchAnyId(array $ids): StateInterface|null - { - if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); - } - - if (null !== ($ids['id'] ?? null)) { - $stmt = $this->pdo->prepare( - sprintf( - 'SELECT * FROM %s WHERE %s = :id LIMIT 1', - $this->escapeIdentifier('state'), - $this->escapeIdentifier('id'), - ) - ); - - if (false === ($stmt->execute(['id' => $ids['id']]))) { - return null; + try { + if (null === ($this->stmt[$cachedKey] ?? null)) { + $this->stmt[$cachedKey] = $this->pdo->prepare("SELECT * FROM state WHERE {$sqlWhere}"); } - if (false === ($row = $stmt->fetch(PDO::FETCH_ASSOC))) { - return null; + if (false === $this->stmt[$cachedKey]->execute($cond)) { + $this->stmt[$cachedKey] = null; + throw new StorageException('Failed to execute sql query.', 61); } - return Container::get(StateInterface::class)::fromArray($row); - } - - $cond = $where = []; - - foreach ($ids as $_val) { - if (null === $_val || !str_starts_with($_val, 'guid_')) { - continue; + if (false === ($row = $this->stmt[$cachedKey]->fetch(PDO::FETCH_ASSOC))) { + return null; } - [$key, $val] = explode('://', $_val); - - $cond[$key] = $val; - } - - if (empty($cond)) { - return null; - } - - foreach ($cond as $key => $_) { - $where[] = $this->escapeIdentifier($key) . ' = :' . $key; - } - - $sqlWhere = implode(' OR ', $where); - - $stmt = $this->pdo->prepare( - sprintf( - "SELECT * FROM %s WHERE %s LIMIT 1", - $this->escapeIdentifier('state'), - $sqlWhere - ) - ); - - if (false === $stmt->execute($cond)) { - throw new RuntimeException('Unable to prepare sql statement'); - } - - if (false === ($row = $stmt->fetch(PDO::FETCH_ASSOC))) { - return null; + return $class::fromArray($row); + } catch (PDOException|StorageException $e) { + $this->stmt[$cachedKey] = null; + throw $e; } - - return Container::get(StateInterface::class)::fromArray($row); } public function remove(StateInterface $entity): bool { + if (null === $this->pdo) { + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); + } + if (null === $entity->id && !$entity->hasGuids()) { return false; } try { if (null === $entity->id) { - if (null === $dbEntity = $this->get($entity)) { + if (null === ($dbEntity = $this->get($entity))) { return false; } $id = $dbEntity->id; @@ -309,20 +280,9 @@ public function remove(StateInterface $entity): bool $id = $entity->id; } - if (null === $this->stmtDelete) { - $this->stmtDelete = $this->pdo->prepare( - sprintf( - 'DELETE FROM %s WHERE %s = :id', - $this->escapeIdentifier('state'), - $this->escapeIdentifier('id'), - ) - ); - } - - $this->stmtDelete->execute(['id' => $id]); + $this->pdo->query('DELETE FROM state WHERE id = ' . (int)$id); } catch (PDOException $e) { $this->logger->error($e->getMessage()); - $this->stmtDelete = null; return false; } @@ -332,7 +292,7 @@ public function remove(StateInterface $entity): bool public function commit(array $entities): array { if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); } return $this->transactional(function () use ($entities) { @@ -377,6 +337,45 @@ public function commit(array $entities): array }); } + public function migrations(string $dir, InputInterface $input, OutputInterface $output, array $opts = []): mixed + { + if (null === $this->pdo) { + throw new StorageException('Setup(): method was not called.', StorageException::SETUP_NOT_CALLED); + } + + $class = new PDOMigrations($this->pdo); + + return match (strtolower($dir)) { + StorageInterface::MIGRATE_UP => $class->up($input, $output), + StorageInterface::MIGRATE_DOWN => $class->down($output), + default => throw new StorageException(sprintf('Unknown direction \'%s\' was given.', $dir), 91), + }; + } + + /** + * @throws Exception + */ + public function makeMigration(string $name, OutputInterface $output, array $opts = []): mixed + { + if (null === $this->pdo) { + throw new StorageException('Setup(): method was not called.'); + } + + return (new PDOMigrations($this->pdo))->make($name, $output); + } + + public function maintenance(InputInterface $input, OutputInterface $output, array $opts = []): mixed + { + return (new PDOMigrations($this->pdo))->runMaintenance(); + } + + public function setLogger(LoggerInterface $logger): StorageInterface + { + $this->logger = $logger; + + return $this; + } + /** * Wrap Transaction. * @@ -409,6 +408,22 @@ private function transactional(Closure $callback): mixed } } + /** + * Get PDO Driver. + * + * @return string + */ + private function getDriver(): string + { + $driver = $this->pdo->getAttribute($this->pdo::ATTR_DRIVER_NAME); + + if (empty($driver) || !is_string($driver)) { + $driver = 'unknown'; + } + + return strtolower($driver); + } + /** * Generate SQL Insert Statement. * @@ -418,7 +433,7 @@ private function transactional(Closure $callback): mixed */ private function pdoInsert(string $table, array $columns): string { - $queryString = 'INSERT INTO ' . $this->escapeIdentifier($table) . ' (%{columns}) VALUES(%{values})'; + $queryString = "INSERT INTO {$table} (%(columns)) VALUES(%(values))"; $sql_columns = $sql_placeholder = []; @@ -427,12 +442,12 @@ private function pdoInsert(string $table, array $columns): string continue; } - $sql_columns[] = $this->escapeIdentifier($column, true); - $sql_placeholder[] = ':' . $this->escapeIdentifier($column, false); + $sql_columns[] = $column; + $sql_placeholder[] = ':' . $column; } $queryString = str_replace( - ['%{columns}', '%{values}'], + ['%(columns)', '%(values)'], [implode(', ', $sql_columns), implode(', ', $sql_placeholder)], $queryString ); @@ -449,11 +464,8 @@ private function pdoInsert(string $table, array $columns): string */ private function pdoUpdate(string $table, array $columns): string { - $queryString = sprintf( - 'UPDATE %s SET ${place} = ${holder} WHERE %s = :id', - $this->escapeIdentifier($table, true), - $this->escapeIdentifier('id', true) - ); + /** @noinspection SqlWithoutWhere */ + $queryString = "UPDATE {$table} SET %(place) = %(holder) WHERE id = :id"; $placeholders = []; @@ -461,95 +473,14 @@ private function pdoUpdate(string $table, array $columns): string if ('id' === $column) { continue; } - $placeholders[] = sprintf( - '%1$s = :%2$s', - $this->escapeIdentifier($column, true), - $this->escapeIdentifier($column, false) - ); + $placeholders[] = sprintf('%1$s = :%1$s', $column); } - return trim(str_replace('${place} = ${holder}', implode(', ', $placeholders), $queryString)); - } - - private function escapeIdentifier(string $text, bool $quote = true): string - { - // table or column has to be valid ASCII name. - // this is opinionated, but we only allow [a-zA-Z0-9_] in column/table name. - if (!preg_match('#\w#', $text)) { - throw new RuntimeException( - sprintf( - 'Invalid identifier "%s": Column/table must be valid ASCII code.', - $text - ) - ); - } - - // The first character cannot be [0-9]: - if (preg_match('/^\d/', $text)) { - throw new RuntimeException( - sprintf( - 'Invalid identifier "%s": Must begin with a letter or underscore.', - $text - ) - ); - } - - if (!$quote) { - return $text; - } - - return match ($this->driver) { - 'mssql' => '[' . $text . ']', - 'mysql' => '`' . $text . '`', - default => '"' . $text . '"', - }; + return trim(str_replace('%(place) = %(holder)', implode(', ', $placeholders), $queryString)); } public function __destruct() { - $this->stmtDelete = $this->stmtUpdate = $this->stmtInsert = null; - } - - public function migrations(string $dir, InputInterface $input, OutputInterface $output, array $opts = []): mixed - { - if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); - } - - $class = new PDOMigrations($this->pdo); - - return match ($dir) { - StorageInterface::MIGRATE_UP => $class->up($input, $output), - StorageInterface::MIGRATE_DOWN => $class->down($output), - default => throw new RuntimeException(sprintf('Unknown direction \'%s\' was given.', $dir)), - }; - } - - /** - * @throws Exception - */ - public function makeMigration(string $name, OutputInterface $output, array $opts = []): void - { - if (null === $this->pdo) { - throw new RuntimeException('Setup(): method was not called.'); - } - - (new PDOMigrations($this->pdo))->make($name, $output); - } - - public function maintenance(InputInterface $input, OutputInterface $output, array $opts = []): mixed - { - return (new PDOMigrations($this->pdo))->runMaintenance(); - } - - private function getDriver(): string - { - $driver = $this->pdo->getAttribute($this->pdo::ATTR_DRIVER_NAME); - - if (empty($driver) || !is_string($driver)) { - $driver = 'unknown'; - } - - return strtolower($driver); + $this->stmt = []; } } diff --git a/src/Libs/Storage/StorageException.php b/src/Libs/Storage/StorageException.php new file mode 100644 index 00000000..9491088e --- /dev/null +++ b/src/Libs/Storage/StorageException.php @@ -0,0 +1,12 @@ + + */ + public function getAll(DateTimeInterface|null $date = null, StateInterface|null $class = null): array; /** * Update Entity immediately. @@ -52,13 +63,14 @@ public function insert(StateInterface $entity): StateInterface; public function update(StateInterface $entity): StateInterface; /** - * Get Entity. + * Get Entity Using array of ids. * - * @param StateInterface $entity + * @param array $ids + * @param StateInterface|null $class Create object based on given class, if null use default class. * * @return StateInterface|null */ - public function get(StateInterface $entity): StateInterface|null; + public function matchAnyId(array $ids, StateInterface|null $class = null): StateInterface|null; /** * Remove Entity. @@ -78,24 +90,6 @@ public function remove(StateInterface $entity): bool; */ public function commit(array $entities): array; - /** - * Load All Entities From backend. - * - * @param DateTimeInterface|null $date Get Entities That has changed since given time. - * - * @return array - */ - public function getAll(DateTimeInterface|null $date = null): array; - - /** - * Get Entity Using array of ids. - * - * @param array $ids - * - * @return StateInterface|null - */ - public function matchAnyId(array $ids): StateInterface|null; - /** * Migrate Backend Storage Schema. * @@ -124,6 +118,17 @@ public function maintenance(InputInterface $input, OutputInterface $output, arra * @param string $name * @param OutputInterface $output * @param array $opts + * + * @return mixed can return migration file name in supported cases. + */ + public function makeMigration(string $name, OutputInterface $output, array $opts = []): mixed; + + /** + * Inject Logger. + * + * @param LoggerInterface $logger + * @return $this */ - public function makeMigration(string $name, OutputInterface $output, array $opts = []): void; + public function setLogger(LoggerInterface $logger): self; + } diff --git a/src/Libs/helpers.php b/src/Libs/helpers.php index ef19502a..db9b5378 100644 --- a/src/Libs/helpers.php +++ b/src/Libs/helpers.php @@ -3,10 +3,15 @@ declare(strict_types=1); use App\Libs\Config; +use App\Libs\Container; use App\Libs\Extends\Date; +use App\Libs\HttpException; +use App\Libs\Servers\ServerInterface; +use App\Libs\Storage\StorageInterface; use Nyholm\Psr7\Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Log\LoggerInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; @@ -112,7 +117,7 @@ function ag_set(array $array, string $path, mixed $value, string $separator = '. if (is_array($at)) { $at[array_shift($keys)] = $value; } else { - throw new RuntimeException("Can not set value at this path ($path) because is not array."); + throw new RuntimeException("Can not set value at this path ($path) because its not array."); } } else { $path = array_shift($keys); @@ -268,3 +273,87 @@ function httpClientChunks(ResponseStreamInterface $responseStream): Generator } } } + +if (!function_exists('serveHttpRequest')) { + /** + * @throws ReflectionException + */ + function serveHttpRequest(ServerRequestInterface $request): ResponseInterface + { + try { + if (true !== Config::get('webhook.enabled', false)) { + throw new HttpException('Webhook is disabled via config.', 500); + } + + if (null === Config::get('webhook.apikey', null)) { + throw new HttpException('No webhook.apikey is set in config.', 500); + } + + // -- get apikey from header or query. + $apikey = $request->getHeaderLine('x-apikey'); + if (empty($apikey)) { + $apikey = ag($request->getQueryParams(), 'apikey', ''); + if (empty($apikey)) { + throw new HttpException('No API key was given.', 400); + } + } + + if (!hash_equals(Config::get('webhook.apikey'), $apikey)) { + throw new HttpException('Invalid API key was given.', 401); + } + + if (null === ($type = ag($request->getQueryParams(), 'type', null))) { + throw new HttpException('No type was given via type= query.', 400); + } + + $types = Config::get('supported', []); + + if (null === ($backend = ag($types, $type))) { + throw new HttpException('Invalid server type was given.', 400); + } + + $class = new ReflectionClass($backend); + + if (!$class->implementsInterface(ServerInterface::class)) { + throw new HttpException('Invalid Parser Class.', 500); + } + + /** @var ServerInterface $backend */ + $entity = $backend::parseWebhook($request); + + if (null === $entity || !$entity->hasGuids()) { + return new Response(status: 200, headers: ['X-Status' => 'No GUIDs.']); + } + + $storage = Container::get(StorageInterface::class); + + if (null === ($backend = $storage->get($entity))) { + $storage->insert($entity); + return jsonResponse(status: 200, body: $entity->getAll()); + } + + if ($backend->updated > $entity->updated) { + return new Response( + status: 200, + headers: ['X-Status' => 'Entity date is older than what available in storage.'] + ); + } + + if ($backend->apply($entity)->isChanged()) { + $backend = $storage->update($backend); + + return jsonResponse(status: 200, body: $backend->getAll()); + } + + return new Response(status: 200, headers: ['X-Status' => 'Entity is unchanged.']); + } catch (HttpException $e) { + Container::get(LoggerInterface::class)->error($e->getMessage()); + + if (200 === $e->getCode()) { + return new Response(status: $e->getCode(), headers: ['X-Status' => $e->getMessage()]); + } + + return jsonResponse(status: $e->getCode(), body: ['error' => true, 'message' => $e->getMessage()]); + } + } +} diff --git a/tests/Storage/PDOAdapterTest.php b/tests/Storage/PDOAdapterTest.php new file mode 100644 index 00000000..e9303b2c --- /dev/null +++ b/tests/Storage/PDOAdapterTest.php @@ -0,0 +1,313 @@ + null, + 'type' => StateInterface::TYPE_EPISODE, + 'updated' => 0, + 'watched' => 1, + 'meta' => [], + 'guid_plex' => StateInterface::TYPE_EPISODE . '/1', + 'guid_imdb' => StateInterface::TYPE_EPISODE . '/2', + 'guid_tvdb' => StateInterface::TYPE_EPISODE . '/3', + 'guid_tmdb' => StateInterface::TYPE_EPISODE . '/4', + 'guid_tvmaze' => StateInterface::TYPE_EPISODE . '/5', + 'guid_tvrage' => StateInterface::TYPE_EPISODE . '/6', + 'guid_anidb' => StateInterface::TYPE_EPISODE . '/7', + ]; + + private array $testMovie = [ + 'id' => null, + 'type' => StateInterface::TYPE_MOVIE, + 'updated' => 1, + 'watched' => 1, + 'meta' => [], + 'guid_plex' => StateInterface::TYPE_MOVIE . '/10', + 'guid_imdb' => StateInterface::TYPE_MOVIE . '/20', + 'guid_tvdb' => StateInterface::TYPE_MOVIE . '/30', + 'guid_tmdb' => StateInterface::TYPE_MOVIE . '/40', + 'guid_tvmaze' => StateInterface::TYPE_MOVIE . '/50', + 'guid_tvrage' => StateInterface::TYPE_MOVIE . '/60', + 'guid_anidb' => StateInterface::TYPE_MOVIE . '/70', + ]; + + private StorageInterface|null $storage = null; + + public function setUp(): void + { + $this->output = new NullOutput(); + $this->input = new ArrayInput([]); + + $this->storage = new PDOAdapter(new CliLogger($this->output)); + $this->storage->setUp(['dsn' => 'sqlite::memory:']); + $this->storage->migrations('up', $this->input, $this->output); + } + + /** StorageInterface::setUp */ + public function test_setup_throw_exception_if_no_dsn(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(10); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->setUp([]); + } + + public function test_setup_throw_exception_if_invalid_dsn(): void + { + $this->expectException(PDOException::class); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->setUp(['dsn' => 'not_real_driver::foo']); + } + + /** StorageInterface::insert */ + public function test_insert_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + + $storage->insert(new StateEntity([])); + } + + public function test_insert_throw_exception_if_has_id(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(21); + $item = new StateEntity($this->testEpisode); + $this->storage->insert($item); + $this->storage->insert($item); + } + + public function test_insert_successful(): void + { + $item = $this->storage->insert(new StateEntity($this->testEpisode)); + $this->assertSame(1, $item->id); + } + + /** StorageInterface::get */ + public function test_get_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->get(new StateEntity([])); + } + + public function test_get_conditions(): void + { + $item = new StateEntity($this->testEpisode); + + // -- db should be empty at this stage. as such we expect null. + $this->assertNull($this->storage->get($item)); + + // -- insert and return object and assert it's the same + $modified = $this->storage->insert(clone $item); + + $this->assertSame($modified->getAll(), $this->storage->get($item)->getAll()); + + // -- look up based on id + $this->assertSame($modified->getAll(), $this->storage->get($modified)->getAll()); + } + + /** StorageInterface::getAll */ + public function test_getAll_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->getAll(); + } + + public function test_getAll_call_without_initialized_container(): void + { + $this->expectException(Error::class); + $this->expectExceptionMessage('Call to a member function'); + $this->storage->getAll(); + } + + public function test_getAll_conditions(): void + { + $item = new StateEntity($this->testEpisode); + + $this->assertSame([], $this->storage->getAll(class: $item)); + + $this->storage->insert($item); + + $this->assertCount(1, $this->storage->getAll(class: $item)); + + // -- future date should be 0. + $this->assertCount(0, $this->storage->getAll(date: new DateTimeImmutable('now'), class: $item)); + } + + /** StorageInterface::update */ + public function test_update_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->update(new StateEntity([])); + } + + public function test_update_call_without_id_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(51); + $item = new StateEntity($this->testEpisode); + + $this->storage->update($item); + } + + public function test_update_conditions(): void + { + $item = $this->storage->insert(new StateEntity($this->testEpisode)); + $item->guid_plex = StateInterface::TYPE_EPISODE . '/1000'; + + $updatedItem = $this->storage->update($item); + + $this->assertSame($item, $updatedItem); + $this->assertSame($updatedItem->getAll(), $this->storage->get($item)->getAll()); + } + + /** StorageInterface::update */ + public function test_matchAnyId_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->matchAnyId([]); + } + + public function test_matchAnyId_call_without_initialized_container(): void + { + $this->expectException(Error::class); + $this->expectExceptionMessage('Call to a member function'); + $this->storage->matchAnyId([]); + } + + public function test_matchAnyId_conditions(): void + { + $item1 = new StateEntity($this->testEpisode); + $item2 = new StateEntity($this->testMovie); + + $this->assertNull( + $this->storage->matchAnyId( + array_intersect_key($item1->getAll(), array_flip(StateInterface::ENTITY_GUIDS)), + $item1 + ) + ); + + $newItem1 = $this->storage->insert($item1); + $newItem2 = $this->storage->insert($item2); + + $this->assertSame( + $newItem1->getAll(), + $this->storage->matchAnyId( + array_intersect_key($item1->getAll(), array_flip(StateInterface::ENTITY_GUIDS)), + $item1 + )->getAll() + ); + + $this->assertSame( + $newItem2->getAll(), + $this->storage->matchAnyId( + array_intersect_key($item2->getAll(), array_flip(StateInterface::ENTITY_GUIDS)), + $item2 + )->getAll() + ); + } + + /** StorageInterface::remove */ + public function test_remove_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->remove(new StateEntity([])); + } + + public function test_remove_conditions(): void + { + $item1 = new StateEntity($this->testEpisode); + $item2 = new StateEntity($this->testMovie); + $item3 = new StateEntity([]); + + $this->assertFalse($this->storage->remove($item1)); + + $item1 = $this->storage->insert($item1); + $this->storage->insert($item2); + + $this->assertTrue($this->storage->remove($item1)); + $this->assertInstanceOf(StateInterface::class, $this->storage->get($item2)); + + // -- remove without id pointer. + $this->assertTrue($this->storage->remove($item2)); + $this->assertFalse($this->storage->remove($item3)); + } + + /** StorageInterface::commit */ + public function test_commit_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->commit([]); + } + + public function test_commit_conditions(): void + { + $item1 = new StateEntity($this->testEpisode); + $item2 = new StateEntity($this->testMovie); + + $this->assertSame( + [ + StateInterface::TYPE_MOVIE => ['added' => 1, 'updated' => 0, 'failed' => 0], + StateInterface::TYPE_EPISODE => ['added' => 1, 'updated' => 0, 'failed' => 0], + ], + $this->storage->commit([$item1, $item2]) + ); + + $item1->guid_anidb = StateInterface::TYPE_EPISODE . '/1'; + $item2->guid_anidb = StateInterface::TYPE_MOVIE . '/1'; + + $this->assertSame( + [ + StateInterface::TYPE_MOVIE => ['added' => 0, 'updated' => 1, 'failed' => 0], + StateInterface::TYPE_EPISODE => ['added' => 0, 'updated' => 1, 'failed' => 0], + ], + $this->storage->commit([$item1, $item2]) + ); + } + + /** StorageInterface::migrations */ + public function test_migrations_call_without_setup_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(StorageException::SETUP_NOT_CALLED); + $storage = new PDOAdapter(new CliLogger($this->output)); + $storage->migrations('f', new ArrayInput([]), new NullOutput()); + } + public function test_migrations_call_with_wrong_direction_exception(): void + { + $this->expectException(StorageException::class); + $this->expectExceptionCode(91); + $this->storage->migrations('not_dd', new ArrayInput([]), new NullOutput()); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..9f2d88bd --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,15 @@ + Date: Sat, 19 Feb 2022 21:28:54 +0300 Subject: [PATCH 4/5] ci phpunit --- .github/workflows/build.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90742755..9cc07ff6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,9 +7,41 @@ on: - 'dev' tags: - 'v*' + pull_request: jobs: + unit-tests: + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [ 8.1 ] + stability: [ prefer-stable ] + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo, mbstring, ctype, curl, sqlite3 + coverage: none + tools: composer:v2 + - name: get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: restore cached dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ matrix.php }}-${{ matrix.stability }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ matrix.php }}-${{ matrix.stability }}-composer- + - run: composer install --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + - run: composer run test + publish_docker_images: + needs: unit-tests + if: github.event_name != 'pull_request' runs-on: "ubuntu-latest" steps: - name: Checkout From 1789a78643510cb4ced2acc150a2e247b8a809a2 Mon Sep 17 00:00:00 2001 From: "Abdulmhsen B. A. A" Date: Sat, 19 Feb 2022 21:31:04 +0300 Subject: [PATCH 5/5] fixes --- .github/workflows/build.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cc07ff6..578e60d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,13 +11,12 @@ on: jobs: unit-tests: - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + name: PHP ${{ matrix.php }} runs-on: ubuntu-latest strategy: fail-fast: true matrix: php: [ 8.1 ] - stability: [ prefer-stable ] steps: - uses: actions/checkout@v2 - name: Setup PHP @@ -34,9 +33,9 @@ jobs: uses: actions/cache@v2 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-${{ matrix.stability }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-${{ matrix.stability }}-composer- - - run: composer install --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ matrix.php }}-composer- + - run: composer install --prefer-dist --no-interaction --no-progress - run: composer run test publish_docker_images: