diff --git a/composer.json b/composer.json index aaf6cd0..8e5c333 100644 --- a/composer.json +++ b/composer.json @@ -21,12 +21,16 @@ "ext-ctype": "*", "ext-zip": "*", "ext-fileinfo": "*", - "symfony/console": "^6.3" + "symfony/console": "^6.3", + "symfony/dependency-injection": "^6.3", + "symfony/config": "^6.3", + "symfony/yaml": "^6.3" }, "require-dev": { "phpunit/phpunit": "^10", "squizlabs/php_codesniffer": "^3.7", - "symfony/dotenv": "^6.3" + "symfony/dotenv": "^6.3", + "symfony/var-dumper": "^6.3" }, "bin": [ "bin/file-diff" diff --git a/composer.lock b/composer.lock index ec0796b..c461d1d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,125 +1,200 @@ { - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "890ecb29ad69b4d347f8d27a6f41361e", - "packages": [ - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "754c942bbbe5cbba79c0335c60587ccc", + "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "symfony/config", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "name": "symfony/console", - "version": "v6.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-19T20:22:16+00:00" + }, + { + "name": "symfony/console", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Eases the creation of beautiful and testable command line interfaces", @@ -134,149 +209,293 @@ "source": "https://github.com/symfony/console/tree/v6.3.4" }, "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-08-16T10:10:12+00:00" + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-16T10:10:12+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "68a5a9570806a087982f383f6109c5e925892a49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/68a5a9570806a087982f383f6109c5e925892a49", + "reference": "68a5a9570806a087982f383f6109c5e925892a49", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.2.10" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.1", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", + "symfony/yaml": "<5.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.1", + "symfony/expression-language": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "name": "symfony/deprecation-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-16T17:55:17+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", "portable" ], "support": { @@ -699,72 +918,218 @@ "source": "https://github.com/symfony/string/tree/v6.3.2" }, "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-05T08:41:27+00:00" + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-05T08:41:27+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "df1f8aac5751871b83d30bf3e2c355770f8f0691" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/df1f8aac5751871b83d30bf3e2c355770f8f0691", + "reference": "df1f8aac5751871b83d30bf3e2c355770f8f0691", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } - ], - "packages-dev": [ + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.4" + }, + "funding": [ { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-16T18:14:47+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e23292e8c07c85b971b44c1c4b87af52133e2add", + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-31T07:08:24+00:00" + } + ], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ @@ -2394,82 +2759,166 @@ "source": "https://github.com/symfony/dotenv/tree/v6.3.0" }, "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-04-21T14:41:17+00:00" + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T14:41:17+00:00" }, + { + "name": "symfony/var-dumper", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2027be14f8ae8eae999ceadebcda5b4909b81d45", + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "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" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-24T14:51:05+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" } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">8.2", - "ext-sodium": "*", - "ext-ctype": "*", - "ext-zip": "*", + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">8.2", + "ext-sodium": "*", + "ext-ctype": "*", + "ext-zip": "*", "ext-fileinfo": "*" }, "platform-dev": [], diff --git a/phpunit.xml b/phpunit.xml index fe6069b..2bf0535 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,7 @@ - + @@ -7,12 +9,9 @@ tests + tests/Base/BaseTest.php + - - - src - - diff --git a/src/CsvFileHandler.php b/src/CsvFileHandler.php new file mode 100644 index 0000000..9d7710e --- /dev/null +++ b/src/CsvFileHandler.php @@ -0,0 +1,178 @@ +getRows($filename) as $row) { + if ($keyword === $row[$column]) { + return ($format === FileHandler::ARRAY_FORMAT) ? $row : true; + } + } + return false; + } + + + public function toJson(string $filename): string + { + $data = $this->toArray($filename); + + return json_encode($data); + } + + public function toArray(string $filename): array + { + if (!file_exists($filename)) { + throw new FileHandlerException('file not found'); + } + + return iterator_to_array($this->getRows($filename)); + } + + public function findAndReplaceInCsv( + string $filename, + string $keyword, + string $replace, + string|null $column = null + ): bool { + $headers = $this->extractHeader($filename); + + if (!$headers) { + throw new FileHandlerException('failed to extract header'); + } + + $tempFilePath = $this->tempFileHandler->createTempFileWithHeaders($headers); + + + try { + $count = 0; + foreach ($this->getRows($filename, $headers) as $row) { + $count += (!$column) + ? $this->replaceKeywordInRow($row, $keyword, $replace) + : $this->replaceKeywordInColumn($row, $column, $keyword, $replace); + + $this->tempFileHandler->writeRowToTempFile($tempFilePath, $row); + } + + + if ($count < 1) { + return false; + } + + $this->tempFileHandler->renameTempFile($tempFilePath, $filename); + } finally { + $this->tempFileHandler->cleanupTempFile($tempFilePath); + } + + return true; + } + + private function extractHeader(mixed $file): array|false + { + $headers = []; + if (is_resource($file)) { + $headers = fgetcsv($file); + } + if (is_string($file)) { + if (!file_exists($file)) { + return false; + } + try { + $file = fopen($file, 'r'); + $headers = fgetcsv($file); + } finally { + fclose($file); + } + } + + if (!$headers) { + return false; + } + + if (!$this->isValidCsvFileFormat($headers)) { + return false; + } + + + return $headers; + } + + private function isValidCsvFileFormat(array $row): bool + { + if (count($row) <= 1) { + return false; + } + return true; + } + + private function getRows(string $filename): Generator + { + $csvFile = fopen($filename, 'r'); + $headers = $this->extractHeader($csvFile); + + + $isEmptyFile = true; + try { + while (($row = fgetcsv($csvFile)) !== false) { + $isEmptyFile = false; + if (!$this->isValidCsvFileFormat($row)) { + throw new FileHandlerException('invalid csv file format'); + } + $item = array_combine($headers, $row); + + yield $item; + } + } finally { + fclose($csvFile); + } + + + if ($isEmptyFile) { + throw new FileHandlerException('invalid csv file format'); + } + } + + private function replaceKeywordInRow(array &$row, string $keyword, string $replace): int + { + $count = 0; + $replacement = array_search($keyword, $row); + + if ($replacement !== false) { + $row[$replacement] = $replace; + $count++; + } + + return $count; + } + + private function replaceKeywordInColumn(array &$row, string $column, string $keyword, string $replace): int + { + $count = 0; + + if ($keyword === $row[$column]) { + $row[$column] = $replace; + $count++; + } + + return $count; + } +} diff --git a/src/FileHandler.php b/src/FileHandler.php index a2709f1..5067053 100644 --- a/src/FileHandler.php +++ b/src/FileHandler.php @@ -3,15 +3,14 @@ namespace rcsofttech85\FileHandler; use finfo; -use Generator; use rcsofttech85\FileHandler\Exception\FileHandlerException; use ZipArchive; class FileHandler { - const ARRAY_FORMAT = 'array'; + public const ARRAY_FORMAT = 'array'; - private array $files = []; + private null|array $files = []; /** @@ -19,7 +18,7 @@ class FileHandler */ public function open( string $filename, - string $mode = "r+", + string $mode = "w", bool $include_path = false, $context = null ): self { @@ -40,6 +39,9 @@ public function open( */ public function write(string $data, ?int $length = null): void { + if (!$this->files) { + throw new FileHandlerException('no files available to write'); + } foreach ($this->files as $file) { $byteWritten = fwrite($file, $data, $length); if (!$byteWritten) { @@ -116,35 +118,20 @@ public function decompress(string $zipFilename, string $extractPath = "./"): voi */ public function close(): void { + if (!$this->files) { + throw new FileHandlerException('no files are opened'); + } foreach ($this->files as $file) { if (!fclose($file)) { throw new FileHandlerException('file was not closed'); } } + $this->resetFiles(); } - public function searchInCsvFile(string $keyword, string $column, string|null $format = null): bool|array - { - return $this->search($keyword, $column, $format); - } - - /** - * @throws FileHandlerException - */ - public function toArray(): array - { - return iterator_to_array($this->getRows()); - } - - - /** - * @throws FileHandlerException - */ - public function toJson(): string + public function resetFiles(): void { - $data = $this->toArray(); - - return json_encode($data); + $this->files = null; } /** @@ -158,187 +145,21 @@ public function delete(string $filename): void unlink($filename); } - /** - * @throws FileHandlerException - */ - private function getRows(string|null $filename = null): Generator - { - $file = $this->ensureSingleFileProcessing($filename); - $headers = $this->extractHeader($file); - - $isEmptyFile = true; - try { - while (($row = fgetcsv($file)) !== false) { - $isEmptyFile = false; - $this->isValidCsvFileFormat($row); - $item = array_combine($headers, $row); - - yield $item; - } - } finally { - fclose($file); - } - - - if ($isEmptyFile) { - throw new FileHandlerException('invalid file format'); - } - } - private function ensureSingleFileProcessing(string|null $filename): mixed + public function getSingleFileProcessing(string|null $filename): mixed { - if (count($this->files) < 1) { - if (!$filename || !file_exists($filename)) { - throw new FileHandlerException("no files to process"); + if (empty($this->files)) { + if ($filename && file_exists($filename)) { + $this->open($filename); + return $this->files[0]; } - $this->open($filename); + throw new FileHandlerException("No files to process or file not found: $filename"); } if (count($this->files) > 1) { - throw new FileHandlerException("multiple files not allowed"); - } - return $this->files[0]; - } - - /** - * @throws FileHandlerException - */ - private function search(string $keyword, string $column, string|null $format): bool|array - { - foreach ($this->getRows() as $row) { - if ($keyword === $row[$column]) { - return ($format === self::ARRAY_FORMAT) ? $row : true; - } - } - return false; - } - - public function findAndReplaceInCsv( - string $filename, - string $keyword, - string $replace, - string|null $column = null - ): bool { - $headers = $this->extractHeader($filename); - - - if (!$headers) { - throw new FileHandlerException('failed to extract header'); - } - - $tempFilePath = $this->createTempFileWithHeaders($headers); - - try { - $count = 0; - foreach ($this->getRows($filename) as $row) { - if (!$column) { - $count += $this->replaceKeywordInRow($row, $keyword, $replace); - } else { - $count += $this->replaceKeywordInColumn($row, $column, $keyword, $replace); - } - - $this->writeRowToTempFile($tempFilePath, $row); - } - - if ($count < 1) { - return false; - } - - $this->renameTempFile($tempFilePath, $filename); - } finally { - $this->cleanupTempFile($tempFilePath); - } - - return true; - } - - private function replaceKeywordInRow(array &$row, string $keyword, string $replace): int - { - $count = 0; - $replacement = array_search($keyword, $row); - - if ($replacement !== false) { - $row[$replacement] = $replace; - $count++; - } - - return $count; - } - - private function replaceKeywordInColumn(array &$row, string $column, string $keyword, string $replace): int - { - $count = 0; - - if ($keyword === $row[$column]) { - $row[$column] = $replace; - $count++; - } - - return $count; - } - - private function writeRowToTempFile(string $tempFilePath, array $row): void - { - $tempFileHandle = fopen($tempFilePath, 'a'); - fputs($tempFileHandle, implode(',', $row) . PHP_EOL); - fclose($tempFileHandle); - } - - private function renameTempFile(string $tempFilePath, string $filename): void - { - if (!rename($tempFilePath, $filename)) { - throw new FileHandlerException('Failed to rename temp file'); - } - } - - private function cleanupTempFile(string $tempFilePath): void - { - if (file_exists($tempFilePath)) { - unlink($tempFilePath); - } - } - - private function createTempFileWithHeaders(array $headers): string - { - $tempFilePath = tempnam(sys_get_temp_dir(), 'tempfile_'); - $tempFileHandle = fopen($tempFilePath, 'w'); - fputs($tempFileHandle, implode(',', $headers) . PHP_EOL); - fclose($tempFileHandle); - - return $tempFilePath; - } - - - /** - * @throws FileHandlerException - */ - private function isValidCsvFileFormat(array|false $row): void - { - if (!$row || count($row) <= 1) { - throw new FileHandlerException('invalid file format'); - } - } - - private function extractHeader(mixed $file): array|false - { - if (is_resource($file)) { - $headers = fgetcsv($file); - } - if (is_string($file)) { - if (!file_exists($file)) { - return false; - } - try { - $file = fopen($file, 'r'); - $headers = fgetcsv($file); - } finally { - fclose($file); - } + throw new FileHandlerException("Multiple files not allowed"); } - if ($this->isValidCsvFileFormat($headers) !== false) { - return $headers; - } - return false; + return $this->files[0]; } } diff --git a/src/FileHashChecker.php b/src/FileHashChecker.php index 405e8db..dd08ef1 100644 --- a/src/FileHashChecker.php +++ b/src/FileHashChecker.php @@ -13,7 +13,7 @@ class FileHashChecker * @param string $filename * @throws HashException */ - public function __construct(private readonly string $filename) + public function __construct(private readonly string $filename, private readonly CsvFileHandler $csvFileHandler) { if (!file_exists($this->filename)) { throw new HashException('file not found'); @@ -29,20 +29,17 @@ public function __construct(private readonly string $filename) * @throws HashException */ - public function verifyHash(object $fileHandler, string $storedHashesFile, string $algo = self::ALGO_256): bool + public function verifyHash(string $storedHashesFile, string $algo = self::ALGO_256): bool { - if (!$fileHandler instanceof FileHandler) { - throw new HashException("object must be instance of " . FileHandler::class); - } - if (!$storedHashesFile) { throw new HashException('file not found'); } - $file = $fileHandler->open(filename: $storedHashesFile)->searchInCsvFile( - $this->filename, - 'File', - FileHandler::ARRAY_FORMAT + $file = $this->csvFileHandler->searchInCsvFile( + filename: $storedHashesFile, + keyword: $this->filename, + column: 'File', + format: FileHandler::ARRAY_FORMAT ); if (!$file) { diff --git a/src/TempFileHandler.php b/src/TempFileHandler.php new file mode 100644 index 0000000..e54c1e6 --- /dev/null +++ b/src/TempFileHandler.php @@ -0,0 +1,39 @@ +load('services.yaml'); + + $content = "Film,Genre,Lead Studio,Audience score %,Profitability,Rotten Tomatoes %,Worldwide Gross,Year\n" + . "Zack and Miri Make a Porno,Romance,The Weinstein Company,70,1.747541667,64,$41.94 ,2008\n" + . "Youth in Revolt,Comedy,The Weinstein Company,52,1.09,68,$19.62 ,2010\n" + . "Twilight,Romance,Independent,68,6.383363636,26,$702.17 ,2011"; + + file_put_contents('movie.csv', $content); + } + + public static function tearDownAfterClass(): void + { + parent::tearDownAfterClass(); + foreach (static::$files as $file) { + if (file_exists($file)) { + unlink(filename: $file); + } + } + static::$files = null; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ebf2d4f..d2bd446 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,3 +1,5 @@ csvFileHandler = self::$containerBuilder->get('csv_file_handler'); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->csvFileHandler = null; + } + + #[Test] + public function findAndReplaceInCsvMethodShouldReplaceTextWithoutColumnOption() + { + $hasReplaced = $this->csvFileHandler->findAndReplaceInCsv("movie.csv", "Twilight", "Inception"); + + $this->assertTrue($hasReplaced); + $this->assertStringContainsString('Inception', file_get_contents('movie.csv')); + } + + #[Test] + public function findAndReplaceInCsvMethodShouldReplaceTextUsingColumnOption() + { + $hasReplaced = $this->csvFileHandler->findAndReplaceInCsv("movie.csv", "Inception", "Twilight", "Film"); + + $this->assertTrue($hasReplaced); + $this->assertStringContainsString('Twilight', file_get_contents('movie.csv')); + } + + #[Test] + #[DataProvider('provideMovieNames')] + public function searchByKeyword(string $keyword) + { + $isMovieAvailable = $this->csvFileHandler->searchInCsvFile( + filename: "movie.csv", + keyword: $keyword, + column: 'Film' + ); + $this->assertTrue($isMovieAvailable); + } + + #[Test] + #[DataProvider('provideStudioNames')] + public function searchBystudioName(string $keyword) + { + $isStudioFound = $this->csvFileHandler->searchInCsvFile( + filename: "movie.csv", + keyword: $keyword, + column: 'Lead Studio' + ); + $this->assertTrue($isStudioFound); + } + + #[Test] + public function toArrayMethodReturnsValidArray() + { + $data = $this->csvFileHandler->toArray("movie.csv"); + $expected = [ + 'Film' => 'Zack and Miri Make a Porno', + 'Genre' => 'Romance', + 'Lead Studio' => 'The Weinstein Company', + 'Audience score %' => '70', + 'Profitability' => '1.747541667', + 'Rotten Tomatoes %' => '64', + 'Worldwide Gross' => '$41.94 ', + 'Year' => '2008' + + ]; + + $this->assertEquals($expected, $data[0]); + } + + #[Test] + public function searchByKeywordAndReturnArray() + { + $expected = [ + 'Film' => 'Zack and Miri Make a Porno', + 'Genre' => 'Romance', + 'Lead Studio' => 'The Weinstein Company', + 'Audience score %' => '70', + 'Profitability' => '1.747541667', + 'Rotten Tomatoes %' => '64', + 'Worldwide Gross' => '$41.94 ', + 'Year' => '2008' + + ]; + + $data = $this->csvFileHandler->searchInCsvFile( + filename: "movie.csv", + keyword: 'Zack and Miri Make a Porno', + column: 'Film', + format: FileHandler::ARRAY_FORMAT + ); + + $this->assertEquals($expected, $data); + } + + + #[Test] + public function toJsonMethodReturnsValidJsonFormat() + { + $jsonData = $this->csvFileHandler->toJson("movie.csv"); + + $expectedData = '[{"Film":"Zack and Miri Make a Porno","Genre":"Romance","Lead Studio":"The Weinstein Company","Audience score %":"70","Profitability":"1.747541667","Rotten Tomatoes %":"64","Worldwide Gross":"$41.94 ","Year":"2008"},{"Film":"Youth in Revolt","Genre":"Comedy","Lead Studio":"The Weinstein Company","Audience score %":"52","Profitability":"1.09","Rotten Tomatoes %":"68","Worldwide Gross":"$19.62 ","Year":"2010"},{"Film":"Twilight","Genre":"Romance","Lead Studio":"Independent","Audience score %":"68","Profitability":"6.383363636","Rotten Tomatoes %":"26","Worldwide Gross":"$702.17 ","Year":"2011"}]'; + + $this->assertJson($jsonData); + $this->assertJsonStringEqualsJsonString($expectedData, $jsonData); + } + + + #[Test] + #[DataProvider('fileProvider')] + public function throwErrorIfFileFormatIsInvalid(string $file) + { + $this->expectException(FileHandlerException::class); + $this->expectExceptionMessage('invalid csv file format'); + + try { + $this->csvFileHandler->searchInCsvFile( + filename: $file, + keyword: 'Twilight', + column: 'Summit' + ); + } finally { + unlink($file); + } + } + + public static function provideStudioNames(): iterable + { + yield ["The Weinstein Company"]; + yield ["Independent"]; + } + + public static function provideMovieNames(): iterable + { + yield ["Zack and Miri Make a Porno"]; + yield ["Youth in Revolt"]; + yield ["Twilight"]; + } + + public static function fileProvider(): iterable + { + $file1 = 'file1.txt'; + $file2 = 'file2.txt'; + $file3 = 'file3.txt'; + + file_put_contents($file1, "film,year"); + file_put_contents($file2, "film\nyear"); + file_put_contents($file3, "Film"); + + + yield [$file1]; + yield [$file2]; + yield [$file3]; + } +} diff --git a/tests/unit/FileEncryptorTest.php b/tests/unit/FileEncryptorTest.php index 5b7db54..a3bb983 100644 --- a/tests/unit/FileEncryptorTest.php +++ b/tests/unit/FileEncryptorTest.php @@ -2,22 +2,20 @@ namespace unit; +use Base\BaseTest; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; use rcsofttech85\FileHandler\Exception\FileEncryptorException; use rcsofttech85\FileHandler\FileEncryptor; -use Symfony\Component\Dotenv\Dotenv; -class FileEncryptorTest extends TestCase +class FileEncryptorTest extends BaseTest { - private FileEncryptor|null $fileEncryptor; + private FileEncryptor|null $fileEncryptor = null; - private static string $secret; protected function setUp(): void { - $this->fileEncryptor = new FileEncryptor('movie.csv', self::$secret); parent::setUp(); + $this->fileEncryptor = self::$containerBuilder->get('file_encryptor'); } protected function tearDown(): void @@ -26,29 +24,18 @@ protected function tearDown(): void $this->fileEncryptor = null; } - public static function setUpBeforeClass(): void { - $dotenv = new Dotenv(); - $dotenv->load('.env'); - self::$secret = $_ENV['SECRET_KEY']; - - - $content = "Film,Genre,Lead Studio,Audience score %,Profitability,Rotten Tomatoes %,Worldwide Gross,Year\n" - . "Zack and Miri Make a Porno,Romance,The Weinstein Company,70,1.747541667,64,$41.94 ,2008\n" - . "Youth in Revolt,Comedy,The Weinstein Company,52,1.09,68,$19.62 ,2010\n" - . "Twilight,Romance,Independent,68,6.383363636,26,$702.17 ,2011"; - - file_put_contents('movie.csv', $content); + parent::setUpBeforeClass(); + static::$files = ['movie.csv']; } public static function tearDownAfterClass(): void { parent::tearDownAfterClass(); - - unlink("movie.csv"); } + #[Test] public function throwExceptionOnDecryptingNonEncryptedFile() { @@ -76,10 +63,11 @@ public function throwExceptionIfAlreadyEncrypted() #[Test] public function throwExceptionIfDecryptionFails() { - $this->fileEncryptor = new FileEncryptor("movie.csv", 'wrong'); + $fileEncryptor = new FileEncryptor('movie.csv', 'wrong'); + $this->expectException(FileEncryptorException::class); $this->expectExceptionMessage('could not decrypt file'); - $this->fileEncryptor->decryptFile(); + $fileEncryptor->decryptFile(); } #[Test] diff --git a/tests/unit/FileHandlerTest.php b/tests/unit/FileHandlerTest.php index 403b7a8..dfdf436 100644 --- a/tests/unit/FileHandlerTest.php +++ b/tests/unit/FileHandlerTest.php @@ -2,54 +2,37 @@ namespace unit; -use PHPUnit\Framework\Attributes\DataProvider; +use Base\BaseTest; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; use rcsofttech85\FileHandler\Exception\FileHandlerException; use rcsofttech85\FileHandler\FileHandler; -use TypeError; -class FileHandlerTest extends TestCase +class FileHandlerTest extends BaseTest { - private FileHandler|null $fileHandler; + private FileHandler|null $fileHandler = null; protected function setUp(): void { parent::setUp(); - $this->fileHandler = new FileHandler(); + + $this->fileHandler = self::$containerBuilder->get('file_handler'); } public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); - $content = "Film,Genre,Lead Studio,Audience score %,Profitability,Rotten Tomatoes %,Worldwide Gross,Year\n" - . "Zack and Miri Make a Porno,Romance,The Weinstein Company,70,1.747541667,64,$41.94 ,2008\n" - . "Youth in Revolt,Comedy,The Weinstein Company,52,1.09,68,$19.62 ,2010\n" - . "Twilight,Romance,Independent,68,6.383363636,26,$702.17 ,2011"; - - fopen(filename: "file", mode: "w"); - fopen(filename: "file1", mode: "w"); - file_put_contents('movie.csv', $content); + static::$files = ["movie.csv", "file", 'file1']; } public static function tearDownAfterClass(): void { parent::tearDownAfterClass(); - - $files = ["file", "movie.csv", 'file1']; - - foreach ($files as $file) { - if (file_exists($file)) { - unlink(filename: $file); - } - } } protected function tearDown(): void { parent::tearDown(); - + $this->fileHandler->resetFiles(); $this->fileHandler = null; } @@ -69,7 +52,7 @@ public function shouldThrowExceptionIfFileIsNotFound() { $this->expectException(FileHandlerException::class); $this->expectExceptionMessage('File not found'); - $this->fileHandler->open(filename: 'unknown'); + $this->fileHandler->open(filename: 'unknown', mode: "r"); } #[Test] @@ -83,71 +66,17 @@ public function shouldThrowExceptionIfFileIsNotWritable() $this->fileHandler->close(); } - - #[Test] - public function fileIsClosedProperly() - { - $this->fileHandler->open(filename: 'file'); - $this->fileHandler->write(data: "hello world"); - $this->fileHandler->close(); - - $this->expectException(TypeError::class); - $this->fileHandler->write(data: "fwrite(): supplied resource is not a valid stream resource"); - } - - - #[Test] - public function multipleFileCanBeWrittenSimultaneously() - { - $this->fileHandler->open(filename: 'file'); - - $this->fileHandler->open(filename: 'file1'); - - $this->fileHandler->write(data: "hello world"); - - $this->assertEquals("hello world", file_get_contents(filename: 'file')); - - $this->assertEquals("hello world", file_get_contents(filename: 'file1')); - $this->fileHandler->close(); - } - - - #[Test] - #[DataProvider('provideMovieNames')] - #[TestDox('search result with name $keyword exists in file.')] - public function resultFoundForExactNameMatch(string $keyword) - { - $isMovieAvailable = $this->fileHandler->open(filename: 'movie.csv')->searchInCsvFile( - keyword: $keyword, - column: 'Film' - ); - $this->assertTrue($isMovieAvailable); - } - - #[Test] - #[DataProvider('provideStudioNames')] - #[TestDox('search result with name $keyword exists in file.')] - public function studioIsFoundForExactNameMatch(string $keyword) - { - $isStudioFound = $this->fileHandler->open(filename: 'movie.csv')->searchInCsvFile( - keyword: $keyword, - column: 'Lead Studio' - ); - $this->assertTrue($isStudioFound); - } - - #[Test] public function successfulCompression() { $testFile = 'movie.csv'; - $compressedZipFilename = 'compressed.zip'; + $compressedZip = 'compressed.zip'; - $this->fileHandler->compress($testFile, $compressedZipFilename); + $this->fileHandler->compress($testFile, $compressedZip); - $mimeType = $this->fileHandler->getMimeType($compressedZipFilename); + $mimeType = $this->fileHandler->getMimeType($compressedZip); - $this->assertFileExists($compressedZipFilename); + $this->assertFileExists($compressedZip); $this->assertEquals('application/zip', $mimeType); } @@ -164,10 +93,10 @@ public function getMimeTypeFunctionReturnsCorrectInfo() #[Test] public function successfulDecompression() { - $compressedZipFilename = 'compressed.zip'; + $compressedZip = 'compressed.zip'; $extractPath = 'extracted_contents'; - $this->fileHandler->decompress($compressedZipFilename, $extractPath); + $this->fileHandler->decompress($compressedZip, $extractPath); $expectedContent = "Film,Genre,Lead Studio,Audience score %,Profitability,Rotten Tomatoes %,Worldwide Gross,Year\n" . "Zack and Miri Make a Porno,Romance,The Weinstein Company,70,1.747541667,64,$41.94 ,2008\n" @@ -177,143 +106,35 @@ public function successfulDecompression() $this->assertEquals($expectedContent, file_get_contents("./extracted_contents/movie.csv")); - unlink($compressedZipFilename); + unlink($compressedZip); unlink("./extracted_contents/movie.csv"); rmdir($extractPath); } #[Test] - public function toArrayMethodReturnsValidArray() - { - $data = $this->fileHandler->open(filename: 'movie.csv')->toArray(); - $expected = [ - 'Film' => 'Zack and Miri Make a Porno', - 'Genre' => 'Romance', - 'Lead Studio' => 'The Weinstein Company', - 'Audience score %' => '70', - 'Profitability' => '1.747541667', - 'Rotten Tomatoes %' => '64', - 'Worldwide Gross' => '$41.94 ', - 'Year' => '2008' - - ]; - - $this->assertEquals($expected, $data[0]); - } - - #[Test] - public function toJsonMethodReturnsValidJsonFormat() - { - $jsonData = $this->fileHandler->open(filename: 'movie.csv')->toJson(); - - $expectedData = '[{"Film":"Zack and Miri Make a Porno","Genre":"Romance","Lead Studio":"The Weinstein Company","Audience score %":"70","Profitability":"1.747541667","Rotten Tomatoes %":"64","Worldwide Gross":"$41.94 ","Year":"2008"},{"Film":"Youth in Revolt","Genre":"Comedy","Lead Studio":"The Weinstein Company","Audience score %":"52","Profitability":"1.09","Rotten Tomatoes %":"68","Worldwide Gross":"$19.62 ","Year":"2010"},{"Film":"Twilight","Genre":"Romance","Lead Studio":"Independent","Audience score %":"68","Profitability":"6.383363636","Rotten Tomatoes %":"26","Worldwide Gross":"$702.17 ","Year":"2011"}]'; - - $this->assertJson($jsonData); - $this->assertJsonStringEqualsJsonString($expectedData, $jsonData); - } - - - #[Test] - public function searchByKeywordAndReturnArray() + public function fileIsClosedProperly() { - $expected = [ - 'Film' => 'Zack and Miri Make a Porno', - 'Genre' => 'Romance', - 'Lead Studio' => 'The Weinstein Company', - 'Audience score %' => '70', - 'Profitability' => '1.747541667', - 'Rotten Tomatoes %' => '64', - 'Worldwide Gross' => '$41.94 ', - 'Year' => '2008' - - ]; - - $data = $this->fileHandler->open(filename: 'movie.csv')->searchInCsvFile( - keyword: 'Zack and Miri Make a Porno', - column: 'Film', - format: FileHandler::ARRAY_FORMAT - ); - - $this->assertEquals($expected, $data); - } + $this->fileHandler->open(filename: 'file'); + $this->fileHandler->write(data: "hello world"); + $this->fileHandler->close(); - #[Test] - #[DataProvider('fileProvider')] - public function throwErrorIfFileFormatIsInvalid(string $file) - { $this->expectException(FileHandlerException::class); - $this->expectExceptionMessage('invalid file format'); - - try { - $this->fileHandler->open($file)->searchInCsvFile( - keyword: 'Twilight', - column: 'Summit' - ); - } finally { - unlink($file); - } - } - - #[Test] - public function findAndReplaceInCsvMethodShouldReplaceTextUsingColumnOption() - { - $fileHandler = new FileHandler(); - - $hasReplaced = $fileHandler->findAndReplaceInCsv("movie.csv", "Twilight", "Inception", "Film"); - - $this->assertTrue($hasReplaced); - - - $data = $this->fileHandler->open("movie.csv")->searchInCsvFile("Inception", "Film", FileHandler::ARRAY_FORMAT); - - $this->assertEquals($data["Film"], "Inception"); + $this->expectExceptionMessage('no files available to write'); + $this->fileHandler->write(data: "hello"); } #[Test] - public function findAndReplaceInCsvMethodShouldReplaceTextWithoutColumnOption() - { - $fileHandler = new FileHandler(); - - - $hasReplaced = $fileHandler->findAndReplaceInCsv("movie.csv", "Inception", "Twilight"); - - $this->assertTrue($hasReplaced); - - - $data = $this->fileHandler->open("movie.csv")->searchInCsvFile("Twilight", "Film", FileHandler::ARRAY_FORMAT); - - $this->assertEquals($data["Film"], "Twilight"); - } - - // Data Providers - - public static function provideStudioNames(): iterable - { - yield ["The Weinstein Company"]; - yield ["Independent"]; - } - - - public static function provideMovieNames(): iterable + public function multipleFileCanBeWrittenSimultaneously() { - yield ["Zack and Miri Make a Porno"]; - yield ["Youth in Revolt"]; - yield ["Twilight"]; - } + $this->fileHandler->open(filename: 'file'); - public static function fileProvider(): iterable - { - $file1 = 'file1.txt'; - $file2 = 'file2.txt'; - $file3 = 'file3.txt'; + $this->fileHandler->open(filename: 'file1'); - file_put_contents($file1, "film,year"); - file_put_contents($file2, "film\nyear"); - file_put_contents($file3, "Film"); + $this->fileHandler->write(data: "hello world"); + $this->assertEquals("hello world", file_get_contents(filename: 'file')); - yield [$file1]; - yield [$file2]; - yield [$file3]; + $this->assertEquals("hello world", file_get_contents(filename: 'file1')); + $this->fileHandler->close(); } } diff --git a/tests/unit/FileHashCheckerTest.php b/tests/unit/FileHashCheckerTest.php index d1e9c62..ba8d3d5 100644 --- a/tests/unit/FileHashCheckerTest.php +++ b/tests/unit/FileHashCheckerTest.php @@ -2,52 +2,59 @@ namespace unit; +use Base\BaseTest; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use rcsofttech85\FileHandler\CsvFileHandler; use rcsofttech85\FileHandler\Exception\HashException; -use rcsofttech85\FileHandler\FileHandler; use rcsofttech85\FileHandler\FileHashChecker; use Symfony\Component\Dotenv\Dotenv; -class FileHashCheckerTest extends TestCase +class FileHashCheckerTest extends BaseTest { private static string $file; - private FileHashChecker|null $fileHasher = null; + private FileHashChecker|null $fileHash = null; + + + protected function setUp(): void + { + parent::setUp(); + + $this->fileHash = self::$containerBuilder->get('file_hash'); + } + + protected function tearDown(): void + { + parent::tearDown(); + $this->fileHash = null; + } public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); - $dotenv = new Dotenv(); $dotenv->load('.env'); - $file = $_ENV['FILE_NAME']; // this file contains list of all hashes - - self::$file = $file; - - - file_put_contents("test", "hello world"); + self::$file = $_ENV['FILE_NAME']; + static::$files = ['movie.csv', 'sample']; } public static function tearDownAfterClass(): void { parent::tearDownAfterClass(); - unlink("test"); - unlink("sample"); } #[Test] public function shouldGenerateValidHashForDifferentAlgo() { - $expectedHash = "644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938"; + $expectedHash = "5923032f7e18edf69e1a3221be3205ce658ec0e4fb274016212a09a804240683"; - $actualHash = $this->fileHasher->hashFile(); //default ALGO_256 + $actualHash = $this->fileHash->hashFile(); //default ALGO_256 $this->assertEquals($expectedHash, $actualHash); - $expectedHash = "840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a"; + $expectedHash = "1050bcc2d7d840d634f067a22abb4cd693b1f2590849982e29a6f9bb28963f73392b63ea24ae17edfaa500ee62b9e5482b9648af0b2b7d941992af3b0f9cbd3b"; - $actualHash = $this->fileHasher->hashFile(FileHashChecker::ALGO_512); + $actualHash = $this->fileHash->hashFile(FileHashChecker::ALGO_512); $this->assertEquals($expectedHash, $actualHash); } @@ -55,7 +62,7 @@ public function shouldGenerateValidHashForDifferentAlgo() #[Test] public function checkFileIntegrityReturnsTrueIfHashMatch() { - $isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file); + $isVerified = $this->fileHash->verifyHash(storedHashesFile: self::$file); $this->assertTrue($isVerified); } @@ -63,20 +70,20 @@ public function checkFileIntegrityReturnsTrueIfHashMatch() #[Test] public function shouldReturnFalseIfFileIsModified() { - $backup = file_get_contents("test"); - file_put_contents("test", "modified", FILE_APPEND); + $backup = file_get_contents("movie.csv"); + file_put_contents("movie.csv", "modified", FILE_APPEND); - $isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file); + $isVerified = $this->fileHash->verifyHash(self::$file); $this->assertfalse($isVerified); - file_put_contents("test", $backup); + file_put_contents("movie.csv", $backup); } #[Test] public function shouldReturnFalseIfDifferentAlgoIsUsedForVerifyHash() { - $isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file, FileHashChecker::ALGO_512); + $isVerified = $this->fileHash->verifyHash(self::$file, FileHashChecker::ALGO_512); $this->assertFalse($isVerified); } @@ -84,23 +91,12 @@ public function shouldReturnFalseIfDifferentAlgoIsUsedForVerifyHash() #[Test] public function shouldThrowExceptionIfFileIsNotHashed() { + /** @var CsvFileHandler $csvFile */ + $csvFile = self::$containerBuilder->get('csv_file_handler'); file_put_contents("sample", "this file is not hashed"); - $this->fileHasher = new FileHashChecker("sample"); - + $this->fileHash = new FileHashChecker("sample", $csvFile); $this->expectException(HashException::class); $this->expectExceptionMessage("this file is not hashed"); - $this->fileHasher->verifyHash(new FileHandler(), self::$file, FileHashChecker::ALGO_512); - } - - protected function setUp(): void - { - parent::setUp(); - $this->fileHasher = new FileHashChecker("test"); - } - - protected function tearDown(): void - { - parent::tearDown(); - $this->fileHasher = null; + $this->fileHash->verifyHash(self::$file, FileHashChecker::ALGO_512); } } diff --git a/tests/unit/TempFileHandlerTest.php b/tests/unit/TempFileHandlerTest.php new file mode 100644 index 0000000..c8cd16a --- /dev/null +++ b/tests/unit/TempFileHandlerTest.php @@ -0,0 +1,88 @@ +tempFileHandler->writeRowToTempFile($tempFilePath, []); + + $this->tempFileHandler->renameTempFile($tempFilePath, $newFileName); + + $this->assertFileExists($newFileName); + $this->assertFileDoesNotExist($tempFilePath); + + unlink($newFileName); + } + + #[Test] + public function writeRowToTempFile(): void + { + $tempFilePath = 'tempfile.txt'; + $row = ['data', 'to', 'write']; + + + $this->tempFileHandler->writeRowToTempFile($tempFilePath, $row); + + $fileContents = file_get_contents($tempFilePath); + $expectedContents = implode(',', $row) . PHP_EOL; + + $this->assertEquals($expectedContents, $fileContents); + + unlink($tempFilePath); + } + + #[Test] + public function cleanupTempFile(): void + { + $tempFilePath = 'tempfile.txt'; + + + $this->tempFileHandler->writeRowToTempFile($tempFilePath, []); + + $this->tempFileHandler->cleanupTempFile($tempFilePath); + + $this->assertFileDoesNotExist($tempFilePath); + } + + #[Test] + public function createTempFileWithHeaders(): void + { + $headers = ['header1', 'header2', 'header3']; + + + $tempFilePath = $this->tempFileHandler->createTempFileWithHeaders($headers); + + $fileContents = file_get_contents($tempFilePath); + $expectedContents = implode(',', $headers) . PHP_EOL; + + $this->assertEquals($expectedContents, $fileContents); + + unlink($tempFilePath); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->tempFileHandler = new TempFileHandler(); + } + + protected function tearDown(): void + { + parent::tearDown(); + $this->tempFileHandler = null; + } +}