From 85c35f88e899e9183f957724f1bf36f6b63e33bc Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Sun, 13 Oct 2024 16:36:45 -0300 Subject: [PATCH 01/12] Initial project setup for KaririCode Validator: - Added attribute-based validation using the `Validate` attribute - Implemented EmailValidator and RangeValidator for common validation tasks - Integrated ContentValidator for pipeline-based entity validation - Added exception handling with ValidationException and PSR-3 logging support --- .docker/php/Dockerfile | 25 + .docker/php/kariricode-php.ini | 14 + .env.example | 3 + .gitattributes | 17 + .github/workflows/kariri-ci-cd.yml | 72 + .gitignore | 66 + .php-cs-fixer.php | 69 + .vscode/settings.json | 10 + Makefile | 174 + README.md | 24 + README.pt-br.md | 24 + composer.json | 49 + composer.lock | 5058 ++++++++++++++++++++++++++++ docker-compose.yml | 15 + phpcs.xml | 22 + phpinsights.php | 60 + phpstan.neon | 7 + phpunit.xml | 37 + psalm.xml | 32 + tests/application.php | 3 + 20 files changed, 5781 insertions(+) create mode 100644 .docker/php/Dockerfile create mode 100644 .docker/php/kariricode-php.ini create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/kariri-ci-cd.yml create mode 100644 .gitignore create mode 100644 .php-cs-fixer.php create mode 100644 .vscode/settings.json create mode 100644 Makefile create mode 100644 README.md create mode 100644 README.pt-br.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 docker-compose.yml create mode 100644 phpcs.xml create mode 100644 phpinsights.php create mode 100644 phpstan.neon create mode 100644 phpunit.xml create mode 100644 psalm.xml create mode 100644 tests/application.php diff --git a/.docker/php/Dockerfile b/.docker/php/Dockerfile new file mode 100644 index 0000000..a3a7de4 --- /dev/null +++ b/.docker/php/Dockerfile @@ -0,0 +1,25 @@ +ARG PHP_VERSION=8.3 + +FROM php:${PHP_VERSION}-alpine + +# Install system dependencies +RUN apk update && apk add --no-cache \ + $PHPIZE_DEPS \ + linux-headers \ + zlib-dev \ + libmemcached-dev \ + cyrus-sasl-dev + +RUN pecl install xdebug redis memcached \ + && docker-php-ext-enable xdebug redis memcached + +# Copy custom PHP configuration +COPY .docker/php/kariricode-php.ini /usr/local/etc/php/conf.d/ + +# Instalação do Composer +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + +RUN apk del --purge $PHPIZE_DEPS && rm -rf /var/cache/apk/* + +# Mantém o contêiner ativo sem fazer nada +CMD tail -f /dev/null diff --git a/.docker/php/kariricode-php.ini b/.docker/php/kariricode-php.ini new file mode 100644 index 0000000..9e90446 --- /dev/null +++ b/.docker/php/kariricode-php.ini @@ -0,0 +1,14 @@ +[PHP] +memory_limit = 256M +upload_max_filesize = 50M +post_max_size = 50M +date.timezone = America/Sao_Paulo + +[Xdebug] +; zend_extension=xdebug.so +xdebug.mode=debug +xdebug.start_with_request=yes +xdebug.client_host=host.docker.internal +xdebug.client_port=9003 +xdebug.log=/tmp/xdebug.log +xdebug.idekey=VSCODE diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e461630 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +KARIRI_APP_ENV=develop +KARIRI_PHP_VERSION=8.3 +KARIRI_PHP_PORT=9003 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..31f41b6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +/.docker export-ignore +/.github export-ignore +/.vscode export-ignore +/tests export-ignore +/vendor export-ignore +/.env export-ignore +/.env.example export-ignore +/.gitignore export-ignore +/.php-cs-fixer.php export-ignore +/.phpcs-cache export-ignore +/docker-compose.yml export-ignore +/phpcs.xml export-ignore +/phpinsights.php export-ignore +/phpstan.neon export-ignore +/phpunit.xml export-ignore +/psalm.xml export-ignore +/Makefile export-ignore \ No newline at end of file diff --git a/.github/workflows/kariri-ci-cd.yml b/.github/workflows/kariri-ci-cd.yml new file mode 100644 index 0000000..bd9f272 --- /dev/null +++ b/.github/workflows/kariri-ci-cd.yml @@ -0,0 +1,72 @@ +name: Kariri CI Pipeline + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + setup-and-lint: + runs-on: ubuntu-latest + strategy: + matrix: + php: ["8.3"] + + steps: + - uses: actions/checkout@v3 + + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, xml + tools: composer:v2, php-cs-fixer, phpunit + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Validate composer.json + run: composer validate + + - name: Coding Standards Check + run: vendor/bin/php-cs-fixer fix --dry-run --diff + + unit-tests: + needs: setup-and-lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Download Composer Cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, xml + tools: composer:v2, php-cs-fixer, phpunit + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHPUnit Tests + run: XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text + + - name: Security Check + run: vendor/bin/security-checker security:check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e5baad --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Arquivos de configuração do sistema +/.idea/ +*.sublime-project +*.sublime-workspace +/.phpunit.result.cache +/.php_cs.cache +/.php_cs.dist.cache +/phpstan.neon.dist +/phpstan.neon.cache +/.phpstan.result.cache +/.phpcs-cache + +# Dependências +/vendor/ +/node_modules/ + +# Arquivos específicos do sistema operacional +.DS_Store +Thumbs.db + +# Arquivos de build e compilação +/build/ +/dist/ +*.log +*.tlog +*.tmp +*.temp + +# Arquivos e pastas de ambientes virtuais +.env + +# Arquivos de cache +/cache/ +*.cache +*.class + +# Arquivos de log +*.log +*.sql +*.sqlite + +# Pasta de testes que não devem ser incluídas no repositório +coverage/ +coverage* + +# Arquivos de pacotes +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# Outros arquivos e pastas +*.swp +*~ +._* +temp/ +tmp/ +.vscode/launch.json +.vscode/extensions.json +tests/lista_de_arquivos.php +tests/lista_de_arquivos_test.php +lista_de_arquivos.txt +lista_de_arquivos_tests.txt +add_static_to_providers.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..c3a51bb --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,69 @@ +in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') + ->exclude('var') + ->exclude('config') + ->exclude('vendor'); + +return (new PhpCsFixer\Config()) + ->setParallelConfig(new PhpCsFixer\Runner\Parallel\ParallelConfig(4, 20)) + ->setRules([ + '@PSR12' => true, + '@Symfony' => true, + 'full_opening_tag' => false, + 'phpdoc_var_without_name' => false, + 'phpdoc_to_comment' => false, + 'array_syntax' => ['syntax' => 'short'], + 'concat_space' => ['spacing' => 'one'], + 'binary_operator_spaces' => [ + 'default' => 'single_space', + 'operators' => [ + '=' => 'single_space', + '=>' => 'single_space', + ], + ], + 'blank_line_before_statement' => [ + 'statements' => ['return'] + ], + 'cast_spaces' => ['space' => 'single'], + 'class_attributes_separation' => [ + 'elements' => [ + 'const' => 'none', + 'method' => 'one', + 'property' => 'none' + ] + ], + 'declare_equal_normalize' => ['space' => 'none'], + 'function_typehint_space' => true, + 'lowercase_cast' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'ordered_imports' => true, + 'phpdoc_align' => ['align' => 'left'], + 'phpdoc_no_alias_tag' => ['replacements' => ['type' => 'var', 'link' => 'see']], + 'phpdoc_order' => true, + 'phpdoc_scalar' => true, + 'single_quote' => true, + 'standardize_not_equals' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'trim_array_spaces' => true, + 'space_after_semicolon' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_whitespace_before_comma_in_array' => true, + 'whitespace_after_comma_in_array' => true, + 'visibility_required' => ['elements' => ['const', 'method', 'property']], + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'method_chaining_indentation' => true, + 'class_definition' => [ + 'single_item_single_line' => false, + 'multi_line_extends_each_single_line' => true, + ], + 'not_operator_with_successor_space' => false + ]) + ->setRiskyAllowed(true) + ->setFinder($finder) + ->setUsingCache(false); diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..38f7f80 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "[php]": { + "editor.defaultFormatter": "junstyle.php-cs-fixer" + }, + "php-cs-fixer.executablePath": "${workspaceFolder}/vendor/bin/php-cs-fixer", + "php-cs-fixer.onsave": true, + "php-cs-fixer.rules": "@PSR12", + "php-cs-fixer.config": ".php_cs.dist", + "php-cs-fixer.formatHtml": true +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f375e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,174 @@ +# Initial configurations +PHP_SERVICE := kariricode-validator +DC := docker-compose + +# Command to execute commands inside the PHP container +EXEC_PHP := $(DC) exec -T php + +# Icons +CHECK_MARK := ✅ +WARNING := ⚠️ +INFO := ℹ️ + +# Colors +RED := \033[0;31m +GREEN := \033[0;32m +YELLOW := \033[1;33m +NC := \033[0m # No Color + +# Check if Docker is installed +CHECK_DOCKER := @command -v docker > /dev/null 2>&1 || { echo >&2 "${YELLOW}${WARNING} Docker is not installed. Aborting.${NC}"; exit 1; } +# Check if Docker Compose is installed +CHECK_DOCKER_COMPOSE := @command -v docker-compose > /dev/null 2>&1 || { echo >&2 "${YELLOW}${WARNING} Docker Compose is not installed. Aborting.${NC}"; exit 1; } +# Function to check if the container is running +CHECK_CONTAINER_RUNNING := @docker ps | grep $(PHP_SERVICE) > /dev/null 2>&1 || { echo >&2 "${YELLOW}${WARNING} The container $(PHP_SERVICE) is not running. Run 'make up' to start it.${NC}"; exit 1; } +# Check if the .env file exists +CHECK_ENV := @test -f .env || { echo >&2 "${YELLOW}${WARNING} .env file not found. Run 'make setup-env' to configure.${NC}"; exit 1; } + +## setup-env: Copy .env.example to .env if the latter does not exist +setup-env: + @test -f .env || (cp .env.example .env && echo "${GREEN}${CHECK_MARK} .env file created successfully from .env.example${NC}") + +check-environment: + @echo "${GREEN}${INFO} Checking Docker, Docker Compose, and .env file...${NC}" + $(CHECK_DOCKER) + $(CHECK_DOCKER_COMPOSE) + $(CHECK_ENV) + +check-container-running: + $(CHECK_CONTAINER_RUNNING) + +## up: Start all services in the background +up: check-environment + @echo "${GREEN}${INFO} Starting services...${NC}" + @$(DC) up -d + @echo "${GREEN}${CHECK_MARK} Services are up!${NC}" + +## down: Stop and remove all containers +down: check-environment + @echo "${YELLOW}${INFO} Stopping and removing services...${NC}" + @$(DC) down + @echo "${GREEN}${CHECK_MARK} Services stopped and removed!${NC}" + +## build: Build Docker images +build: check-environment + @echo "${YELLOW}${INFO} Building services...${NC}" + @$(DC) build + @echo "${GREEN}${CHECK_MARK} Services built!${NC}" + +## logs: Show container logs +logs: check-environment + @echo "${YELLOW}${INFO} Container logs:${NC}" + @$(DC) logs + +## re-build: Rebuild and restart containers +re-build: check-environment + @echo "${YELLOW}${INFO} Stopping and removing current services...${NC}" + @$(DC) down + @echo "${GREEN}${INFO} Rebuilding services...${NC}" + @$(DC) build + @echo "${GREEN}${INFO} Restarting services...${NC}" + @$(DC) up -d + @echo "${GREEN}${CHECK_MARK} Services rebuilt and restarted successfully!${NC}" + @$(DC) logs + +## shell: Access the shell of the PHP container +shell: check-environment check-container-running + @echo "${GREEN}${INFO} Accessing the shell of the PHP container...${NC}" + @$(DC) exec php sh + +## composer-install: Install Composer dependencies. Use make composer-install [PKG="[vendor/package [version]]"] [DEV="--dev"] +composer-install: check-environment check-container-running + @echo "${GREEN}${INFO} Installing Composer dependencies...${NC}" + @if [ -z "$(PKG)" ]; then \ + $(EXEC_PHP) composer install; \ + else \ + $(EXEC_PHP) composer require $(PKG) $(DEV); \ + fi + @echo "${GREEN}${CHECK_MARK} Composer operation completed!${NC}" + +## composer-remove: Remove Composer dependencies. Usage: make composer-remove PKG="vendor/package" +composer-remove: check-environment check-container-running + @if [ -z "$(PKG)" ]; then \ + echo "${RED}${WARNING} You must specify a package to remove. Usage: make composer-remove PKG=\"vendor/package\"${NC}"; \ + else \ + $(EXEC_PHP) composer remove $(PKG); \ + echo "${GREEN}${CHECK_MARK} Package $(PKG) removed successfully!${NC}"; \ + fi + +## composer-update: Update Composer dependencies +composer-update: check-environment check-container-running + @echo "${GREEN}${INFO} Updating Composer dependencies...${NC}" + $(EXEC_PHP) composer update + @echo "${GREEN}${CHECK_MARK} Dependencies updated!${NC}" + +## test: Run tests +test: check-environment check-container-running + @echo "${GREEN}${INFO} Running tests...${NC}" + $(EXEC_PHP) ./vendor/bin/phpunit --testdox --colors=always tests + @echo "${GREEN}${CHECK_MARK} Tests completed!${NC}" + +## test-file: Run tests on a specific class. Usage: make test-file FILE=[file] +test-file: check-environment check-container-running + @echo "${GREEN}${INFO} Running test for class $(FILE)...${NC}" + $(EXEC_PHP) ./vendor/bin/phpunit --testdox --colors=always tests/$(FILE) + @echo "${GREEN}${CHECK_MARK} Test for class $(FILE) completed!${NC}" + +## coverage: Run test coverage with visual formatting +coverage: check-environment check-container-running + @echo "${GREEN}${INFO} Analyzing test coverage...${NC}" + XDEBUG_MODE=coverage $(EXEC_PHP) ./vendor/bin/phpunit --coverage-text --colors=always tests | ccze -A + +## coverage-html: Run test coverage and generate HTML report +coverage-html: check-environment check-container-running + @echo "${GREEN}${INFO} Analyzing test coverage and generating HTML report...${NC}" + XDEBUG_MODE=coverage $(EXEC_PHP) ./vendor/bin/phpunit --coverage-html ./coverage-report-html tests + @echo "${GREEN}${INFO} Test coverage report generated in ./coverage-report-html${NC}" + +## run-script: Run a PHP script. Usage: make run-script SCRIPT="path/to/script.php" +run-script: check-environment check-container-running + @echo "${GREEN}${INFO} Running script: $(SCRIPT)...${NC}" + $(EXEC_PHP) php $(SCRIPT) + @echo "${GREEN}${CHECK_MARK} Script executed!${NC}" + +## cs-check: Run PHP_CodeSniffer to check code style +cs-check: check-environment check-container-running + @echo "${GREEN}${INFO} Checking code style...${NC}" + $(EXEC_PHP) ./vendor/bin/php-cs-fixer fix --dry-run --diff + @echo "${GREEN}${CHECK_MARK} Code style check completed!${NC}" + +## cs-fix: Run PHP CS Fixer to fix code style +cs-fix: check-environment check-container-running + @echo "${GREEN}${INFO} Fixing code style with PHP CS Fixer...${NC}" + $(EXEC_PHP) ./vendor/bin/php-cs-fixer fix + @echo "${GREEN}${CHECK_MARK} Code style fixed!${NC}" + +## security-check: Check for security vulnerabilities in dependencies +security-check: check-environment check-container-running + @echo "${GREEN}${INFO} Checking for security vulnerabilities with Security Checker...${NC}" + $(EXEC_PHP) ./vendor/bin/security-checker security:check + @echo "${GREEN}${CHECK_MARK} Security check completed!${NC}" + +## quality: Run all quality commands +quality: check-environment check-container-running cs-check test security-check + @echo "${GREEN}${CHECK_MARK} All quality commands executed!${NC}" + +## help: Show initial setup steps and available commands +help: + @echo "${GREEN}Initial setup steps for configuring the project:${NC}" + @echo "1. ${YELLOW}Initial environment setup:${NC}" + @echo " ${GREEN}${CHECK_MARK} Copy the environment file:${NC} make setup-env" + @echo " ${GREEN}${CHECK_MARK} Start the Docker containers:${NC} make up" + @echo " ${GREEN}${CHECK_MARK} Install Composer dependencies:${NC} make composer-install" + @echo "2. ${YELLOW}Development:${NC}" + @echo " ${GREEN}${CHECK_MARK} Access the PHP container shell:${NC} make shell" + @echo " ${GREEN}${CHECK_MARK} Run a PHP script:${NC} make run-script SCRIPT=\"script_name.php\"" + @echo " ${GREEN}${CHECK_MARK} Run the tests:${NC} make test" + @echo "3. ${YELLOW}Maintenance:${NC}" + @echo " ${GREEN}${CHECK_MARK} Update Composer dependencies:${NC} make composer-update" + @echo " ${GREEN}${CHECK_MARK} Clear the application cache:${NC} make cache-clear" + @echo " ${RED}${WARNING} Stop and remove all Docker containers:${NC} make down" + @echo "\n${GREEN}Available commands:${NC}" + @sed -n 's/^##//p' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ": "}; {printf "${YELLOW}%-30s${NC} %s\n", $$1, $$2}' + +.PHONY: setup-env up down build logs re-build shell composer-install composer-remove composer-update test test-file coverage coverage-html run-script cs-check cs-fix security-check quality help diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d6bb36 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# KaririCode Framework: Sanitizer Component + +[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](README.pt-br.md) + +![PHP](https://img.shields.io/badge/PHP-777BB4?style=for-the-badge&logo=php&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white) ![PHPUnit](https://img.shields.io/badge/PHPUnit-3776AB?style=for-the-badge&logo=php&logoColor=white) + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Support and Community + +- **Documentation**: [https://kariricode.org/docs/dotenv](https://kariricode.org/docs/dotenv) +- **Issue Tracker**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-validator/issues) +- **Community**: [KaririCode Club Community](https://kariricode.club) + +## Acknowledgments + +- The KaririCode Framework team and contributors. +- Inspired by other popular PHP Dotenv libraries. + +--- + +Built with ❤️ by the KaririCode team. Empowering developers to build more robust and flexible PHP applications. diff --git a/README.pt-br.md b/README.pt-br.md new file mode 100644 index 0000000..46777c0 --- /dev/null +++ b/README.pt-br.md @@ -0,0 +1,24 @@ +# KaririCode Framework: Componente Dotenv + +[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](README.pt-br.md) + +![PHP](https://img.shields.io/badge/PHP-777BB4?style=for-the-badge&logo=php&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white) ![PHPUnit](https://img.shields.io/badge/PHPUnit-3776AB?style=for-the-badge&logo=php&logoColor=white) + +## Licença + +Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para mais detalhes. + +## Suporte e Comunidade + +- **Documentação**: [https://kariricode.org/docs/dotenv](https://kariricode.org/docs/dotenv) +- **Rastreador de Problemas**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-validator/issues) +- **Comunidade**: [Comunidade KaririCode Club](https://kariricode.club) + +## Agradecimentos + +- A equipe do KaririCode Framework e contribuidores. +- Inspirado por outras bibliotecas populares de Dotenv para PHP. + +--- + +Construído com ❤️ pela equipe KaririCode. Capacitando desenvolvedores a criar aplicações PHP mais robustas e flexíveis. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d0fc518 --- /dev/null +++ b/composer.json @@ -0,0 +1,49 @@ +{ + "name": "kariricode/validator", + "description": "A robust and flexible data sanitization component for PHP, part of the KaririCode Framework, utilizing configurable processors and native functions.", + "keywords": [ + "kariricode", + "validator", + "validation", + "data", + "attribute", + "processor", + "pipeline", + "entity", + "php" + ], + "homepage": "https://kariricode.org", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Walmir Silva", + "email": "community@kariricode.org" + } + ], + "require": { + "php": "^8.3", + "kariricode/data-structure": "^1.0" + }, + "autoload": { + "psr-4": { + "KaririCode\\Validator\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "KaririCode\\Validator\\Tests\\": "tests" + } + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.51", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.9", + "enlightn/security-checker": "^2.0" + }, + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-validator/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-validator" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..4330907 --- /dev/null +++ b/composer.lock @@ -0,0 +1,5058 @@ +{ + "_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": "63730a5d4446ee4194b406853f2e0bd1", + "packages": [ + { + "name": "kariricode/contract", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/KaririCode-Framework/kariricode-contract.git", + "reference": "5d98b009c7c5c20dd63b4440405ac81f93544e7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/5d98b009c7c5c20dd63b4440405ac81f93544e7d", + "reference": "5d98b009c7c5c20dd63b4440405ac81f93544e7d", + "shasum": "" + }, + "require": { + "php": "^8.3" + }, + "require-dev": { + "enlightn/security-checker": "^2.0", + "friendsofphp/php-cs-fixer": "^3.58", + "mockery/mockery": "^1.6", + "nunomaduro/phpinsights": "^2.11", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "KaririCode\\Contract\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Walmir Silva", + "email": "walmir.silva@kariricode.org" + } + ], + "description": "Central repository for interface definitions in the KaririCode Framework. Implements interface code in PHP for specified namespaces, adhering to PSR standards and leveraging modern PHP features.", + "homepage": "https://kariricode.org/", + "keywords": [ + "PSRs", + "contract", + "framework", + "interface", + "kariri", + "php" + ], + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-contract/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-contract" + }, + "time": "2024-10-10T21:05:49+00:00" + }, + { + "name": "kariricode/data-structure", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/KaririCode-Framework/kariricode-data-structure.git", + "reference": "91c9e7ef36143b5e3d449f1a132169580945a4c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-data-structure/zipball/91c9e7ef36143b5e3d449f1a132169580945a4c8", + "reference": "91c9e7ef36143b5e3d449f1a132169580945a4c8", + "shasum": "" + }, + "require": { + "kariricode/contract": "^2.0", + "php": "^8.3" + }, + "require-dev": { + "enlightn/security-checker": "^2.0", + "friendsofphp/php-cs-fixer": "^3.58", + "mockery/mockery": "^1.6", + "nunomaduro/phpinsights": "^2.11", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "KaririCode\\DataStructure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Walmir Silva", + "email": "walmir.silva@kariricode.org" + } + ], + "description": "The KaririCode DataStructure component offers advanced PHP data structures, including lists, stacks, queues, maps, and sets. It features efficient, strongly-typed, object-oriented implementations like ArrayList, LinkedList, BinaryHeap, and TreeMap.", + "homepage": "https://kariricode.org/", + "keywords": [ + "KaririCode", + "LinkedList", + "OOP", + "algorithms", + "arraylist", + "binary heap", + "collections", + "data structures", + "dynamic array", + "heap", + "php", + "queue", + "red-black tree", + "set", + "stack", + "strong typing", + "treemap" + ], + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-data-structure/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-data-structure" + }, + "time": "2024-10-10T22:37:23+00:00" + } + ], + "packages-dev": [ + { + "name": "clue/ndjson-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\NDJson\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.10", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-08-27T18:44:43+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "enlightn/security-checker", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/enlightn/security-checker.git", + "reference": "d495ab07639388c7c770c5223aa0d42fee1d2604" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/enlightn/security-checker/zipball/d495ab07639388c7c770c5223aa0d42fee1d2604", + "reference": "d495ab07639388c7c770c5223aa0d42fee1d2604", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.3|^7.0", + "php": ">=8.2", + "symfony/console": "^7", + "symfony/finder": "^3|^4|^5|^6|^7", + "symfony/process": "^3.4|^4|^5|^6|^7", + "symfony/yaml": "^3.4|^4|^5|^6|^7" + }, + "require-dev": { + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^2.18|^3.0", + "phpunit/phpunit": "^5.5|^6|^7|^8|^9" + }, + "bin": [ + "security-checker" + ], + "type": "library", + "autoload": { + "psr-4": { + "Enlightn\\SecurityChecker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paras Malhotra", + "email": "paras@laravel-enlightn.com" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + } + ], + "description": "A PHP dependency vulnerabilities scanner based on the Security Advisories Database.", + "keywords": [ + "package", + "php", + "scanner", + "security", + "security advisories", + "vulnerability scanner" + ], + "support": { + "issues": "https://github.com/enlightn/security-checker/issues", + "source": "https://github.com/enlightn/security-checker/tree/v2.0.0" + }, + "time": "2023-12-10T07:17:09+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.64.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "58dd9c931c785a79739310aef5178928305ffa67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/58dd9c931c785a79739310aef5178928305ffa67", + "reference": "58dd9c931c785a79739310aef5178928305ffa67", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.0", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.3", + "infection/infection": "^0.29.5", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.5", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.5", + "phpunit/phpunit": "^9.6.19 || ^10.5.21 || ^11.2", + "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/Fixer/Internal/*" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.64.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-08-30T23:09:38+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-07-18T10:29:17+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.12.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", + "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-10-06T15:03:59+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.3.1", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.4.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T06:21:38+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7875627f15f4da7e7f0823d1f323f7295a77334e", + "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.6", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.1", + "sebastian/comparator": "^6.1.0", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.1.3", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.0", + "sebastian/version": "^5.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.4-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.1" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-10-08T15:38:37+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.5", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/e71eb1aa55f057c7a4a0d08d06b0b0a484bead43", + "reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/socket": "^1.8", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.5" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-09-16T13:41:56+00:00" + }, + { + "name": "react/dns", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "react/socket", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "6bb7d09d6623567178cf54126afa9c2310114268" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6bb7d09d6623567178cf54126afa9c2310114268", + "reference": "6bb7d09d6623567178cf54126afa9c2310114268", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:44:28+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-09-11T15:42:56+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:54:44+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", + "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:56:19+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:10:34+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-09-17T13:12:04+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.10.3", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", + "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-09-18T10:38:58+00:00" + }, + { + "name": "symfony/console", + "version": "v7.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.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", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.1.5" + }, + "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": "2024-09-20T08:28:38+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-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.5.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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.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": "2024-05-31T14:57:53+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "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/v7.1.5" + }, + "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": "2024-09-17T09:16:35+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.1.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.1.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": "2024-08-13T14:28:19+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "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 an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.1.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": "2024-05-31T14:57:53+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "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": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "5c03ee6369281177f07f7c68252a280beccba847" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", + "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.1.5" + }, + "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": "2024-09-19T21:48:23+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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 a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.1.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": "2024-05-31T14:57:53+00:00" + }, + { + "name": "symfony/string", + "version": "v7.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.1.5" + }, + "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": "2024-09-20T08:28:38+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4e561c316e135e053bd758bf3b3eb291d9919de4", + "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.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/v7.1.5" + }, + "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": "2024-09-17T12:49:58+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "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.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.3" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f9fda5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + php: + container_name: kariricode-validator + build: + context: . + dockerfile: .docker/php/Dockerfile + args: + PHP_VERSION: ${KARIRI_PHP_VERSION} + environment: + XDEBUG_MODE: coverage + volumes: + - .:/app + working_dir: /app + ports: + - "${KARIRI_PHP_PORT}:9003" diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..07143a4 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + src/ + tests/ + + + vendor/* + config/* + tests/bootstrap.php + tests/object-manager.php + + diff --git a/phpinsights.php b/phpinsights.php new file mode 100644 index 0000000..5df088e --- /dev/null +++ b/phpinsights.php @@ -0,0 +1,60 @@ + 'symfony', + 'exclude' => [ + 'src/Migrations', + 'src/Kernel.php', + ], + 'add' => [], + 'remove' => [ + \PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterNotSniff::class, + \NunoMaduro\PhpInsights\Domain\Sniffs\ForbiddenSetterSniff::class, + \SlevomatCodingStandard\Sniffs\Commenting\UselessFunctionDocCommentSniff::class, + \SlevomatCodingStandard\Sniffs\Commenting\DocCommentSpacingSniff::class, + \SlevomatCodingStandard\Sniffs\Classes\SuperfluousInterfaceNamingSniff::class, + \SlevomatCodingStandard\Sniffs\Classes\SuperfluousExceptionNamingSniff::class, + \SlevomatCodingStandard\Sniffs\ControlStructures\DisallowYodaComparisonSniff::class, + \NunoMaduro\PhpInsights\Domain\Insights\ForbiddenTraits::class, + \NunoMaduro\PhpInsights\Domain\Insights\ForbiddenNormalClasses::class, + \SlevomatCodingStandard\Sniffs\Classes\SuperfluousTraitNamingSniff::class, + \SlevomatCodingStandard\Sniffs\Classes\ForbiddenPublicPropertySniff::class, + \NunoMaduro\PhpInsights\Domain\Insights\CyclomaticComplexityIsHigh::class, + \NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineFunctions::class, + \NunoMaduro\PhpInsights\Domain\Insights\ForbiddenFinalClasses::class, + \NunoMaduro\PhpInsights\Domain\Insights\ForbiddenGlobals::class, + \PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\FunctionCommentSniff::class, + \SlevomatCodingStandard\Sniffs\TypeHints\ReturnTypeHintSniff::class, + \SlevomatCodingStandard\Sniffs\Commenting\InlineDocCommentDeclarationSniff::class, + \SlevomatCodingStandard\Sniffs\Classes\ModernClassNameReferenceSniff::class, + \PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\UselessOverridingMethodSniff::class, + \SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff::class, + \SlevomatCodingStandard\Sniffs\TypeHints\ParameterTypeHintSniff::class, + \SlevomatCodingStandard\Sniffs\TypeHints\PropertyTypeHintSniff::class, + \SlevomatCodingStandard\Sniffs\Arrays\TrailingArrayCommaSniff::class + ], + 'config' => [ + \PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff::class => [ + 'lineLimit' => 120, + 'absoluteLineLimit' => 160, + ], + \SlevomatCodingStandard\Sniffs\Commenting\InlineDocCommentDeclarationSniff::class => [ + 'exclude' => [ + 'src/Exception/BaseException.php', + ], + ], + \SlevomatCodingStandard\Sniffs\ControlStructures\AssignmentInConditionSniff::class => [ + 'enabled' => false, + ], + ], + 'requirements' => [ + 'min-quality' => 80, + 'min-complexity' => 50, + 'min-architecture' => 75, + 'min-style' => 95, + 'disable-security-check' => false, + ], + 'threads' => null +]; diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..c3392e9 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: max + paths: + - src + - tests + ignoreErrors: + - '#Method .* has parameter \$.* with no value type specified in iterable type array.#' diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..ba8e7af --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + tests + + + + + + src + + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..f0c90a3 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/application.php b/tests/application.php new file mode 100644 index 0000000..6c8c4f5 --- /dev/null +++ b/tests/application.php @@ -0,0 +1,3 @@ + Date: Thu, 17 Oct 2024 18:47:16 -0300 Subject: [PATCH 02/12] feat(validation): add validation system with multiple processors - Added `Validate` attribute for validation configuration. - Created `ValidationException` for handling validation-related exceptions. - Introduced `AbstractValidatorProcessor` as a base class for all validators. - Added date processors: `DateFormatValidator` and `DateRangeValidator`. - Added input processors: `EmailValidator`, `LengthValidator`, and `UrlValidator`. - Added logic processors: `ConditionalValidator` and `RequiredValidator`. - Added numeric processors: `IntegerValidator` and `RangeValidator`. - Implemented main `Validator` class to manage validations. - Updated `composer.json` and `composer.lock` for dependencies. - Modified `tests/application.php` to include new validation tests. --- composer.json | 6 +- composer.lock | 196 ++++++++++++++++++- src/Attribute/Validate.php | 27 +++ src/Exception/ValidationException.php | 9 + src/Processor/AbstractValidatorProcessor.php | 22 +++ src/Processor/Date/DateFormatValidator.php | 29 +++ src/Processor/Date/DateRangeValidator.php | 58 ++++++ src/Processor/Input/EmailValidator.php | 17 ++ src/Processor/Input/LengthValidator.php | 32 +++ src/Processor/Input/UrlValidator.php | 17 ++ src/Processor/Logic/ConditionalValidator.php | 35 ++++ src/Processor/Logic/RequiredValidator.php | 22 +++ src/Processor/Numeric/IntegerValidator.php | 15 ++ src/Processor/Numeric/RangeValidator.php | 31 +++ src/Validator.php | 55 ++++++ tests/application.php | 128 ++++++++++++ 16 files changed, 690 insertions(+), 9 deletions(-) mode change 100644 => 100755 composer.lock create mode 100644 src/Attribute/Validate.php create mode 100644 src/Exception/ValidationException.php create mode 100644 src/Processor/AbstractValidatorProcessor.php create mode 100644 src/Processor/Date/DateFormatValidator.php create mode 100644 src/Processor/Date/DateRangeValidator.php create mode 100644 src/Processor/Input/EmailValidator.php create mode 100644 src/Processor/Input/LengthValidator.php create mode 100644 src/Processor/Input/UrlValidator.php create mode 100644 src/Processor/Logic/ConditionalValidator.php create mode 100644 src/Processor/Logic/RequiredValidator.php create mode 100644 src/Processor/Numeric/IntegerValidator.php create mode 100644 src/Processor/Numeric/RangeValidator.php create mode 100644 src/Validator.php diff --git a/composer.json b/composer.json index d0fc518..bab3265 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "entity", "php" ], + "version": "1.0.0", "homepage": "https://kariricode.org", "type": "library", "license": "MIT", @@ -23,7 +24,10 @@ ], "require": { "php": "^8.3", - "kariricode/data-structure": "^1.0" + "kariricode/contract": "^2.7", + "kariricode/processor-pipeline": "^1.1", + "kariricode/property-inspector": "^1.0", + "kariricode/exception": "^1.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock old mode 100644 new mode 100755 index 4330907..a308419 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "63730a5d4446ee4194b406853f2e0bd1", + "content-hash": "efe910a586ddd39e4e22d0c319883ed6", "packages": [ { "name": "kariricode/contract", - "version": "v2.6.3", + "version": "v2.7.6", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-contract.git", - "reference": "5d98b009c7c5c20dd63b4440405ac81f93544e7d" + "reference": "98793d561a0c9992b12658aa9941767f4c565c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/5d98b009c7c5c20dd63b4440405ac81f93544e7d", - "reference": "5d98b009c7c5c20dd63b4440405ac81f93544e7d", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/98793d561a0c9992b12658aa9941767f4c565c4d", + "reference": "98793d561a0c9992b12658aa9941767f4c565c4d", "shasum": "" }, "require": { @@ -66,7 +66,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-contract/issues", "source": "https://github.com/KaririCode-Framework/kariricode-contract" }, - "time": "2024-10-10T21:05:49+00:00" + "time": "2024-10-15T22:32:33+00:00" }, { "name": "kariricode/data-structure", @@ -141,6 +141,186 @@ "source": "https://github.com/KaririCode-Framework/kariricode-data-structure" }, "time": "2024-10-10T22:37:23+00:00" + }, + { + "name": "kariricode/exception", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/KaririCode-Framework/kariricode-exception.git", + "reference": "1c562fbe81e59226701ac472064b7897b8951d38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-exception/zipball/1c562fbe81e59226701ac472064b7897b8951d38", + "reference": "1c562fbe81e59226701ac472064b7897b8951d38", + "shasum": "" + }, + "require": { + "php": "^8.3" + }, + "require-dev": { + "enlightn/security-checker": "^2.0", + "friendsofphp/php-cs-fixer": "^3.51", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "KaririCode\\Exception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Walmir Silva", + "email": "community@kariricode.org" + } + ], + "description": "KaririCode Exception provides a robust and modular exception handling system for the KaririCode Framework, enabling seamless error management across various application domains.", + "homepage": "https://kariricode.org", + "keywords": [ + "error-management", + "exception-handling", + "framework", + "kariri-code", + "modular-exceptions", + "php-exceptions", + "php-framework" + ], + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-exception/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-exception" + }, + "time": "2024-10-16T21:30:45+00:00" + }, + { + "name": "kariricode/processor-pipeline", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline.git", + "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/07c645a1c45f47ffd748e6bb8231806b465bd7bc", + "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc", + "shasum": "" + }, + "require": { + "kariricode/contract": "^2.7", + "kariricode/data-structure": "^1.1", + "php": "^8.3" + }, + "require-dev": { + "enlightn/security-checker": "^2.0", + "friendsofphp/php-cs-fixer": "^3.51", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "KaririCode\\ProcessorPipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Walmir Silva", + "email": "community@kariricode.org" + } + ], + "description": "A flexible and extensible processor pipeline component for the KaririCode framework. Enables the creation of modular, configurable processing chains for data transformation, validation, and sanitization tasks", + "homepage": "https://kariricode.org", + "keywords": [ + "KaririCode", + "configurable", + "data processing", + "modular", + "php", + "pipeline", + "processor" + ], + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline" + }, + "time": "2024-10-15T14:03:33+00:00" + }, + { + "name": "kariricode/property-inspector", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/KaririCode-Framework/kariricode-property-inspector.git", + "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-property-inspector/zipball/30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", + "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", + "shasum": "" + }, + "require": { + "kariricode/contract": "^2.7", + "kariricode/processor-pipeline": "^1.1", + "php": "^8.3" + }, + "require-dev": { + "enlightn/security-checker": "^2.0", + "friendsofphp/php-cs-fixer": "^3.51", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "KaririCode\\PropertyInspector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Walmir Silva", + "email": "community@kariricode.org" + } + ], + "description": "A robust and flexible data sanitization component for PHP, part of the KaririCode Framework, utilizing configurable processors and native functions.", + "homepage": "https://kariricode.org", + "keywords": [ + "attribute", + "dynamic-analysis", + "framework", + "inspection", + "kariri-code", + "metadata", + "normalization", + "object-properties", + "php8", + "property-inspector", + "reflection", + "validation" + ], + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-property-inspector/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-property-inspector" + }, + "time": "2024-10-15T16:42:44+00:00" } ], "packages-dev": [ @@ -5047,12 +5227,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^8.3" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/src/Attribute/Validate.php b/src/Attribute/Validate.php new file mode 100644 index 0000000..f8ef4f5 --- /dev/null +++ b/src/Attribute/Validate.php @@ -0,0 +1,27 @@ +validators; + } + + public function getFallbackValue(): mixed + { + return $this->fallbackValue; + } +} diff --git a/src/Exception/ValidationException.php b/src/Exception/ValidationException.php new file mode 100644 index 0000000..ff6aea4 --- /dev/null +++ b/src/Exception/ValidationException.php @@ -0,0 +1,9 @@ +format = $options['format']; + } + } + + public function process(mixed $input): bool + { + $input = $this->guardAgainstNonString($input); + + $date = \DateTime::createFromFormat($this->format, $input); + + return $date && $date->format($this->format) === $input; + } +} diff --git a/src/Processor/Date/DateRangeValidator.php b/src/Processor/Date/DateRangeValidator.php new file mode 100644 index 0000000..6212cca --- /dev/null +++ b/src/Processor/Date/DateRangeValidator.php @@ -0,0 +1,58 @@ +setDate('minDate', $options['minDate']); + $this->setDate('maxDate', $options['maxDate']); + + if (isset($options['format'])) { + $this->format = $options['format']; + } + + if ($this->minDate > $this->maxDate) { + throw new \InvalidArgumentException('minDate cannot be greater than maxDate.'); + } + } + + public function process(mixed $input): bool + { + var_dump($input); + $input = $this->guardAgainstNonString($input); + + $date = \DateTime::createFromFormat($this->format, $input); + if (!$date) { + return false; + } + + $date->setTime(0, 0, 0); + + return $date >= $this->minDate && $date <= $this->maxDate; + } + + private function setDate(string $type, string $dateString): void + { + $date = \DateTime::createFromFormat($this->format, $dateString); + if (!$date) { + throw new \InvalidArgumentException(sprintf('Invalid %s format. Expected format: %s', $type, $this->format)); + } + $date->setTime(0, 0, 0); + $this->{$type} = $date; + } +} diff --git a/src/Processor/Input/EmailValidator.php b/src/Processor/Input/EmailValidator.php new file mode 100644 index 0000000..4d42727 --- /dev/null +++ b/src/Processor/Input/EmailValidator.php @@ -0,0 +1,17 @@ +guardAgainstNonString($input); + + return false !== filter_var($input, FILTER_VALIDATE_EMAIL); + } +} diff --git a/src/Processor/Input/LengthValidator.php b/src/Processor/Input/LengthValidator.php new file mode 100644 index 0000000..57acf8c --- /dev/null +++ b/src/Processor/Input/LengthValidator.php @@ -0,0 +1,32 @@ +minLength = $options['minLength']; + } + if (isset($options['maxLength'])) { + $this->maxLength = $options['maxLength']; + } + } + + public function process(mixed $input): bool + { + $input = $this->guardAgainstNonString($input); + $length = mb_strlen($input); + + return $length >= $this->minLength && $length <= $this->maxLength; + } +} diff --git a/src/Processor/Input/UrlValidator.php b/src/Processor/Input/UrlValidator.php new file mode 100644 index 0000000..c97081f --- /dev/null +++ b/src/Processor/Input/UrlValidator.php @@ -0,0 +1,17 @@ +guardAgainstNonString($input); + + return false !== filter_var($input, FILTER_VALIDATE_URL); + } +} diff --git a/src/Processor/Logic/ConditionalValidator.php b/src/Processor/Logic/ConditionalValidator.php new file mode 100644 index 0000000..4eb9fbf --- /dev/null +++ b/src/Processor/Logic/ConditionalValidator.php @@ -0,0 +1,35 @@ +condition = $options['condition']; + $this->validator = $options['validator']; + } + + public function process(mixed $input): bool + { + if (($this->condition)($input)) { + return ($this->validator)($input); + } + + return true; + } +} diff --git a/src/Processor/Logic/RequiredValidator.php b/src/Processor/Logic/RequiredValidator.php new file mode 100644 index 0000000..dbf7d32 --- /dev/null +++ b/src/Processor/Logic/RequiredValidator.php @@ -0,0 +1,22 @@ +min = $options['min']; + $this->max = $options['max']; + } + + public function process(mixed $input): bool + { + var_dump($input); + $value = (float) $input; + + return $value >= $this->min && $value <= $this->max; + } +} diff --git a/src/Validator.php b/src/Validator.php new file mode 100644 index 0000000..39e15fc --- /dev/null +++ b/src/Validator.php @@ -0,0 +1,55 @@ +builder = new ProcessorBuilder($this->registry); + $this->attributeHandler = new AttributeHandler(self::IDENTIFIER, $this->builder); + $this->propertyInspector = new PropertyInspector( + new AttributeAnalyzer(Validate::class) + ); + } + + public function validate(mixed $object): array + { + try { + var_dump($object, $this->attributeHandler); + $validationResults = $this->propertyInspector->inspect($object, $this->attributeHandler); + + $errors = []; + foreach ($validationResults as $property => $result) { + if (false === $result) { + $errors[$property] = ["Validation failed for {$property}"]; + } elseif (is_string($result)) { + $errors[$property] = [$result]; + } + } + + return $errors; + } catch (PropertyInspectionException $e) { + return ['__exception' => [$e->getMessage()]]; + } + } +} diff --git a/tests/application.php b/tests/application.php index 6c8c4f5..91e9c94 100644 --- a/tests/application.php +++ b/tests/application.php @@ -1,3 +1,131 @@ ['minLength' => 2, 'maxLength' => 50]])] + private string $name = ''; + + #[Validate(validators: ['required', 'email'])] + private string $email = ''; + + #[Validate(validators: ['required', 'integer', 'range' => ['min' => 0, 'max' => 120]])] + private int $age = 0; + + #[Validate(validators: ['url'])] + private string $website = ''; + + #[Validate(validators: ['conditional_phone'])] + private string $phoneNumber = ''; + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getEmail(): string + { + return $this->email; + } + + public function setEmail(string $email): void + { + $this->email = $email; + } + + public function getAge(): int + { + return $this->age; + } + + public function setAge(int $age): void + { + $this->age = $age; + } + + public function getWebsite(): string + { + return $this->website; + } + + public function setWebsite(string $website): void + { + $this->website = $website; + } + + public function getPhoneNumber(): string + { + return $this->phoneNumber; + } + + public function setPhoneNumber(string $phoneNumber): void + { + $this->phoneNumber = $phoneNumber; + } +} + +class Event +{ + #[Validate(validators: ['required', 'date_format' => ['format' => 'Y-m-d']])] + private string $eventDate = ''; + + #[Validate(validators: ['date_range' => ['minDate' => '2023-01-01', 'maxDate' => '2023-12-31', 'format' => 'Y-m-d']])] + private string $registrationDeadline = ''; + + public function getEventDate(): string + { + return $this->eventDate; + } + + public function setEventDate(string $eventDate): void + { + $this->eventDate = $eventDate; + } + + public function getRegistrationDeadline(): string + { + return $this->registrationDeadline; + } + + public function setRegistrationDeadline(string $registrationDeadline): void + { + $this->registrationDeadline = $registrationDeadline; + } +} + +$registry = new ProcessorRegistry(); +$registry->register('validator', 'required', new RequiredValidator()); +$registry->register('validator', 'date_format', new DateFormatValidator()); + +$validator = new Validator($registry); + +$event = new Event(); +$event->setEventDate('2023-13-01'); +$event->setRegistrationDeadline('2024-01-01'); + + +$validationResults = $validator->validate($event); + +// var_dump($validationResults); +// var_dump($event); From 0cc76c3355740d700b3a41af9ac3e1af5d3896d5 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 21 Oct 2024 18:54:08 -0300 Subject: [PATCH 03/12] feat(validation): refactor and improve validation processors - Add new validation result contract and processor interfaces. - Introduce `MissingProcessorConfigException` for better error handling. - Replace `ValidationException` with more specific exception handling. - Update various processors (`LengthValidator`, `EmailValidator`, `UrlValidator`, etc.) to handle configuration properly. - Implement `DefaultValidationResultProcessor` for standardized result processing. - Adjust `DateFormatValidator` and `DateRangeValidator` to improve validation accuracy. - Remove legacy validation exception logic. --- composer.lock | 92 +++---- src/Attribute/Validate.php | 19 +- src/Contract/ValidationResult.php | 20 ++ src/Contract/ValidationResultFactory.php | 10 + src/Contract/ValidationResultProcessor.php | 12 + .../MissingProcessorConfigException.php | 24 ++ src/Exception/ValidationException.php | 9 - src/Processor/AbstractValidatorProcessor.php | 26 +- src/Processor/Date/DateFormatValidator.php | 14 +- src/Processor/Date/DateRangeValidator.php | 49 ++-- .../DefaultValidationResultProcessor.php | 44 ++++ src/Processor/Input/EmailValidator.php | 14 +- src/Processor/Input/LengthValidator.php | 34 ++- src/Processor/Input/UrlValidator.php | 14 +- src/Processor/Logic/ConditionalValidator.php | 16 +- src/Processor/Logic/RequiredValidator.php | 19 +- src/Processor/Numeric/IntegerValidator.php | 8 +- src/Processor/Numeric/RangeValidator.php | 22 +- src/ValidationResult.php | 61 +++++ src/Validator.php | 35 +-- tests/application.php | 232 +++++++++++++----- 21 files changed, 550 insertions(+), 224 deletions(-) create mode 100644 src/Contract/ValidationResult.php create mode 100644 src/Contract/ValidationResultFactory.php create mode 100644 src/Contract/ValidationResultProcessor.php create mode 100644 src/Exception/MissingProcessorConfigException.php delete mode 100644 src/Exception/ValidationException.php create mode 100644 src/Processor/DefaultValidationResultProcessor.php create mode 100644 src/ValidationResult.php diff --git a/composer.lock b/composer.lock index a308419..86f1731 100755 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "kariricode/contract", - "version": "v2.7.6", + "version": "v2.7.10", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-contract.git", - "reference": "98793d561a0c9992b12658aa9941767f4c565c4d" + "reference": "84db138e9e03e7173ee1a37d75fa21d756a6d060" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/98793d561a0c9992b12658aa9941767f4c565c4d", - "reference": "98793d561a0c9992b12658aa9941767f4c565c4d", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/84db138e9e03e7173ee1a37d75fa21d756a6d060", + "reference": "84db138e9e03e7173ee1a37d75fa21d756a6d060", "shasum": "" }, "require": { @@ -66,7 +66,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-contract/issues", "source": "https://github.com/KaririCode-Framework/kariricode-contract" }, - "time": "2024-10-15T22:32:33+00:00" + "time": "2024-10-21T18:32:50+00:00" }, { "name": "kariricode/data-structure", @@ -144,16 +144,16 @@ }, { "name": "kariricode/exception", - "version": "v1.0.0", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-exception.git", - "reference": "1c562fbe81e59226701ac472064b7897b8951d38" + "reference": "65c8eb72c581eb8c33c168e5df104ed260843303" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-exception/zipball/1c562fbe81e59226701ac472064b7897b8951d38", - "reference": "1c562fbe81e59226701ac472064b7897b8951d38", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-exception/zipball/65c8eb72c581eb8c33c168e5df104ed260843303", + "reference": "65c8eb72c581eb8c33c168e5df104ed260843303", "shasum": "" }, "require": { @@ -197,20 +197,20 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-exception/issues", "source": "https://github.com/KaririCode-Framework/kariricode-exception" }, - "time": "2024-10-16T21:30:45+00:00" + "time": "2024-10-17T22:43:32+00:00" }, { "name": "kariricode/processor-pipeline", - "version": "v1.1.4", + "version": "v1.1.5", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline.git", - "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc" + "reference": "6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/07c645a1c45f47ffd748e6bb8231806b465bd7bc", - "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0", + "reference": "6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0", "shasum": "" }, "require": { @@ -256,20 +256,20 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline/issues", "source": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline" }, - "time": "2024-10-15T14:03:33+00:00" + "time": "2024-10-18T18:33:05+00:00" }, { "name": "kariricode/property-inspector", - "version": "v1.0.5", + "version": "v1.1.6", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-property-inspector.git", - "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0" + "reference": "7e70c17b74c69601514fa40a4f76aeb0c056e096" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-property-inspector/zipball/30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", - "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-property-inspector/zipball/7e70c17b74c69601514fa40a4f76aeb0c056e096", + "reference": "7e70c17b74c69601514fa40a4f76aeb0c056e096", "shasum": "" }, "require": { @@ -320,7 +320,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-property-inspector/issues", "source": "https://github.com/KaririCode-Framework/kariricode-property-inspector" }, - "time": "2024-10-15T16:42:44+00:00" + "time": "2024-10-21T20:42:58+00:00" } ], "packages-dev": [ @@ -1019,16 +1019,16 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -1082,7 +1082,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -1098,7 +1098,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1454,16 +1454,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.6", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -1508,7 +1508,7 @@ "type": "github" } ], - "time": "2024-10-06T15:03:59+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1835,16 +1835,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.4.1", + "version": "11.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e" + "reference": "1863643c3f04ad03dcb9c6996c294784cdda4805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7875627f15f4da7e7f0823d1f323f7295a77334e", - "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1863643c3f04ad03dcb9c6996c294784cdda4805", + "reference": "1863643c3f04ad03dcb9c6996c294784cdda4805", "shasum": "" }, "require": { @@ -1858,21 +1858,21 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.6", + "phpunit/php-code-coverage": "^11.0.7", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.1.0", + "sebastian/comparator": "^6.1.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.0", - "sebastian/version": "^5.0.1" + "sebastian/version": "^5.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -1915,7 +1915,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.2" }, "funding": [ { @@ -1931,7 +1931,7 @@ "type": "tidelift" } ], - "time": "2024-10-08T15:38:37+00:00" + "time": "2024-10-19T13:05:19+00:00" }, { "name": "psr/container", @@ -2992,16 +2992,16 @@ }, { "name": "sebastian/comparator", - "version": "6.1.0", + "version": "6.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" + "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5ef523a49ae7a302b87b2102b72b1eda8918d686", + "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686", "shasum": "" }, "require": { @@ -3057,7 +3057,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.1" }, "funding": [ { @@ -3065,7 +3065,7 @@ "type": "github" } ], - "time": "2024-09-11T15:42:56+00:00" + "time": "2024-10-18T15:00:48+00:00" }, { "name": "sebastian/complexity", diff --git a/src/Attribute/Validate.php b/src/Attribute/Validate.php index f8ef4f5..c46e898 100644 --- a/src/Attribute/Validate.php +++ b/src/Attribute/Validate.php @@ -4,24 +4,9 @@ namespace KaririCode\Validator\Attribute; -use KaririCode\Contract\Processor\ProcessableAttribute; +use KaririCode\Contract\Processor\Attribute\BaseProcessorAttribute; #[\Attribute(\Attribute::TARGET_PROPERTY)] -final class Validate implements ProcessableAttribute +final class Validate extends BaseProcessorAttribute { - public function __construct( - public readonly array $validators, - public readonly ?string $fallbackValue = null - ) { - } - - public function getProcessors(): array - { - return $this->validators; - } - - public function getFallbackValue(): mixed - { - return $this->fallbackValue; - } } diff --git a/src/Contract/ValidationResult.php b/src/Contract/ValidationResult.php new file mode 100644 index 0000000..db17665 --- /dev/null +++ b/src/Contract/ValidationResult.php @@ -0,0 +1,20 @@ +isValid = false; + $this->errorKey = $errorKey; + } + + public function isValid(): bool { - if (!is_string($input)) { - throw new ValidationException('Input must be a string'); - } + return $this->isValid; + } - return $input; + public function getErrorKey(): string + { + return $this->errorKey; } - abstract public function process(mixed $input): bool; + abstract public function process(mixed $input): mixed; } diff --git a/src/Processor/Date/DateFormatValidator.php b/src/Processor/Date/DateFormatValidator.php index b5e8e82..72f7890 100644 --- a/src/Processor/Date/DateFormatValidator.php +++ b/src/Processor/Date/DateFormatValidator.php @@ -18,12 +18,20 @@ public function configure(array $options): void } } - public function process(mixed $input): bool + public function process(mixed $input): mixed { - $input = $this->guardAgainstNonString($input); + if (!is_string($input)) { + $this->setInvalid('invalidType'); + + return $input; + } $date = \DateTime::createFromFormat($this->format, $input); - return $date && $date->format($this->format) === $input; + if (!$date || $date->format($this->format) !== $input) { + $this->setInvalid('invalidFormat'); + } + + return $input; } } diff --git a/src/Processor/Date/DateRangeValidator.php b/src/Processor/Date/DateRangeValidator.php index 6212cca..47a8542 100644 --- a/src/Processor/Date/DateRangeValidator.php +++ b/src/Processor/Date/DateRangeValidator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace KaririCode\Validator\Processor\Date; +namespace KaririCode\Validator\Processor\Numeric; use KaririCode\Contract\Processor\ConfigurableProcessor; use KaririCode\Validator\Processor\AbstractValidatorProcessor; @@ -16,43 +16,50 @@ class DateRangeValidator extends AbstractValidatorProcessor implements Configura public function configure(array $options): void { if (!isset($options['minDate']) || !isset($options['maxDate'])) { - throw new \InvalidArgumentException('Both "minDate" and "maxDate" options must be set.'); + throw new \InvalidArgumentException('Both minDate and maxDate must be provided'); } - $this->setDate('minDate', $options['minDate']); - $this->setDate('maxDate', $options['maxDate']); + $this->minDate = $this->parseDate($options['minDate']); + $this->maxDate = $this->parseDate($options['maxDate']); - if (isset($options['format'])) { - $this->format = $options['format']; + if ($this->minDate > $this->maxDate) { + throw new \InvalidArgumentException('minDate must be less than or equal to maxDate'); } - if ($this->minDate > $this->maxDate) { - throw new \InvalidArgumentException('minDate cannot be greater than maxDate.'); + if (isset($options['format'])) { + $this->format = $options['format']; } } - public function process(mixed $input): bool + public function process(mixed $input): mixed { - var_dump($input); - $input = $this->guardAgainstNonString($input); + if (!is_string($input)) { + $this->setInvalid('invalidType'); + + return $input; + } $date = \DateTime::createFromFormat($this->format, $input); - if (!$date) { - return false; + if (!$date || $date->format($this->format) !== $input) { + $this->setInvalid('invalidDate'); + + return $input; } - $date->setTime(0, 0, 0); + if ($date < $this->minDate || $date > $this->maxDate) { + $this->setInvalid('outOfRange'); + } - return $date >= $this->minDate && $date <= $this->maxDate; + return $input; } - private function setDate(string $type, string $dateString): void + private function parseDate(string $date): \DateTimeInterface { - $date = \DateTime::createFromFormat($this->format, $dateString); - if (!$date) { - throw new \InvalidArgumentException(sprintf('Invalid %s format. Expected format: %s', $type, $this->format)); + $parsedDate = \DateTime::createFromFormat($this->format, $date); + if (!$parsedDate) { + throw new \InvalidArgumentException("Invalid date format. Expected: {$this->format}"); } - $date->setTime(0, 0, 0); - $this->{$type} = $date; + + return $parsedDate; } } diff --git a/src/Processor/DefaultValidationResultProcessor.php b/src/Processor/DefaultValidationResultProcessor.php new file mode 100644 index 0000000..75ab6ec --- /dev/null +++ b/src/Processor/DefaultValidationResultProcessor.php @@ -0,0 +1,44 @@ +getProcessedPropertyValues(); + $errors = $handler->getProcessingResultErrors(); + + foreach ($processedValues as $property => $data) { + $this->result->setValidatedData($property, $data['value']); + + if (isset($errors[$property])) { + $this->addPropertyErrors($this->result, $property, $errors[$property]); + } + } + + return $this->result; + } + + private function addPropertyErrors( + ValidationResult $result, + string $property, + array $propertyErrors + ): void { + foreach ($propertyErrors as $error) { + $result->addError($property, $error['errorKey'], $error['message']); + } + } +} diff --git a/src/Processor/Input/EmailValidator.php b/src/Processor/Input/EmailValidator.php index 4d42727..e295e3b 100644 --- a/src/Processor/Input/EmailValidator.php +++ b/src/Processor/Input/EmailValidator.php @@ -8,10 +8,18 @@ class EmailValidator extends AbstractValidatorProcessor { - public function process(mixed $input): bool + public function process(mixed $input): mixed { - $input = $this->guardAgainstNonString($input); + if (!is_string($input)) { + $this->setInvalid('invalidType'); - return false !== filter_var($input, FILTER_VALIDATE_EMAIL); + return $input; + } + + if (false === filter_var($input, FILTER_VALIDATE_EMAIL)) { + $this->setInvalid('invalidFormat'); + } + + return $input; } } diff --git a/src/Processor/Input/LengthValidator.php b/src/Processor/Input/LengthValidator.php index 57acf8c..98cba1b 100644 --- a/src/Processor/Input/LengthValidator.php +++ b/src/Processor/Input/LengthValidator.php @@ -5,6 +5,7 @@ namespace KaririCode\Validator\Processor\Input; use KaririCode\Contract\Processor\ConfigurableProcessor; +use KaririCode\Validator\Exception\MissingProcessorConfigException; use KaririCode\Validator\Processor\AbstractValidatorProcessor; class LengthValidator extends AbstractValidatorProcessor implements ConfigurableProcessor @@ -14,19 +15,38 @@ class LengthValidator extends AbstractValidatorProcessor implements Configurable public function configure(array $options): void { - if (isset($options['minLength'])) { - $this->minLength = $options['minLength']; + if (empty($options)) { + throw MissingProcessorConfigException::missingConfiguration('LengthValidator', 'Configuration is empty'); } - if (isset($options['maxLength'])) { - $this->maxLength = $options['maxLength']; + + if (!isset($options['minLength'])) { + throw MissingProcessorConfigException::missingConfiguration('LengthValidator', 'minLength'); + } + + if (!isset($options['maxLength'])) { + throw MissingProcessorConfigException::missingConfiguration('LengthValidator', 'maxLength'); } + + $this->minLength = $options['minLength']; + $this->maxLength = $options['maxLength']; } - public function process(mixed $input): bool + public function process(mixed $input): mixed { - $input = $this->guardAgainstNonString($input); + if (!is_string($input)) { + $this->setInvalid('invalidType'); + + return $input; + } + $length = mb_strlen($input); - return $length >= $this->minLength && $length <= $this->maxLength; + if ($length < $this->minLength) { + $this->setInvalid('tooShort'); + } elseif ($length > $this->maxLength) { + $this->setInvalid('tooLong'); + } + + return $input; } } diff --git a/src/Processor/Input/UrlValidator.php b/src/Processor/Input/UrlValidator.php index c97081f..e4a37ce 100644 --- a/src/Processor/Input/UrlValidator.php +++ b/src/Processor/Input/UrlValidator.php @@ -8,10 +8,18 @@ class UrlValidator extends AbstractValidatorProcessor { - public function process(mixed $input): bool + public function process(mixed $input): mixed { - $input = $this->guardAgainstNonString($input); + if (!is_string($input)) { + $this->setInvalid('invalidType'); - return false !== filter_var($input, FILTER_VALIDATE_URL); + return $input; + } + + if (false === filter_var($input, FILTER_VALIDATE_URL)) { + $this->setInvalid('invalidFormat'); + } + + return $input; } } diff --git a/src/Processor/Logic/ConditionalValidator.php b/src/Processor/Logic/ConditionalValidator.php index 4eb9fbf..1c34122 100644 --- a/src/Processor/Logic/ConditionalValidator.php +++ b/src/Processor/Logic/ConditionalValidator.php @@ -15,21 +15,25 @@ class ConditionalValidator extends AbstractValidatorProcessor implements Configu public function configure(array $options): void { if (!isset($options['condition']) || !($options['condition'] instanceof \Closure)) { - throw new \InvalidArgumentException('A valid condition closure must be provided.'); + throw new \InvalidArgumentException('Condition must be a Closure'); } + $this->condition = $options['condition']; + if (!isset($options['validator']) || !($options['validator'] instanceof \Closure)) { - throw new \InvalidArgumentException('A valid validator closure must be provided.'); + throw new \InvalidArgumentException('Validator must be a Closure'); } - $this->condition = $options['condition']; $this->validator = $options['validator']; } - public function process(mixed $input): bool + public function process(mixed $input): mixed { if (($this->condition)($input)) { - return ($this->validator)($input); + $validationResult = ($this->validator)($input); + if (true !== $validationResult) { + $this->setInvalid('conditionNotMet'); + } } - return true; + return $input; } } diff --git a/src/Processor/Logic/RequiredValidator.php b/src/Processor/Logic/RequiredValidator.php index dbf7d32..5edaa46 100644 --- a/src/Processor/Logic/RequiredValidator.php +++ b/src/Processor/Logic/RequiredValidator.php @@ -8,15 +8,20 @@ class RequiredValidator extends AbstractValidatorProcessor { - public function process(mixed $input): bool + public function process(mixed $input): mixed { - if (is_string($input)) { - return '' !== trim($input); - } - if (is_numeric($input)) { - return true; + if ($this->isEmpty($input)) { + $this->setInvalid('missingValue'); } - return null !== $input; + return $input; + } + + private function isEmpty(mixed $value): bool + { + return null === $value + || '' === $value + || (is_string($value) && '' === trim($value)) + || (is_array($value) && 0 === count($value)); } } diff --git a/src/Processor/Numeric/IntegerValidator.php b/src/Processor/Numeric/IntegerValidator.php index 3bdca10..c7c4675 100644 --- a/src/Processor/Numeric/IntegerValidator.php +++ b/src/Processor/Numeric/IntegerValidator.php @@ -8,8 +8,12 @@ class IntegerValidator extends AbstractValidatorProcessor { - public function process(mixed $input): bool + public function process(mixed $input): mixed { - return is_int($input) || (is_string($input) && ctype_digit($input)); + if (!is_int($input) && (!is_string($input) || !ctype_digit($input))) { + $this->setInvalid('notAnInteger'); + } + + return $input; } } diff --git a/src/Processor/Numeric/RangeValidator.php b/src/Processor/Numeric/RangeValidator.php index de6d8d4..c9b3b9c 100644 --- a/src/Processor/Numeric/RangeValidator.php +++ b/src/Processor/Numeric/RangeValidator.php @@ -14,18 +14,24 @@ class RangeValidator extends AbstractValidatorProcessor implements ConfigurableP public function configure(array $options): void { - if (!isset($options['min']) || !isset($options['max'])) { - throw new \InvalidArgumentException('Both "min" and "max" options must be set.'); - } - $this->min = $options['min']; - $this->max = $options['max']; + $this->min = $options['min'] ?? PHP_FLOAT_MIN; + $this->max = $options['max'] ?? PHP_FLOAT_MAX; } - public function process(mixed $input): bool + public function process(mixed $input): mixed { - var_dump($input); + if (!is_numeric($input)) { + $this->setInvalid('notNumeric'); + + return $input; + } + $value = (float) $input; - return $value >= $this->min && $value <= $this->max; + if ($value < $this->min || $value > $this->max) { + $this->setInvalid('outOfRange'); + } + + return $value; } } diff --git a/src/ValidationResult.php b/src/ValidationResult.php new file mode 100644 index 0000000..3936aa0 --- /dev/null +++ b/src/ValidationResult.php @@ -0,0 +1,61 @@ +errors[$property])) { + $this->errors[$property] = []; + } + + // Avoid adding duplicate errors + foreach ($this->errors[$property] as $error) { + if ($error['errorKey'] === $errorKey) { + return; + } + } + + $this->errors[$property][] = [ + 'errorKey' => $errorKey, + 'message' => $message, + ]; + } + + public function setValidatedData(string $property, mixed $value): void + { + $this->validatedData[$property] = $value; + } + + public function hasErrors(): bool + { + return !empty($this->errors); + } + + public function getErrors(): array + { + return $this->errors; + } + + public function getValidatedData(): array + { + return $this->validatedData; + } + + public function toArray(): array + { + return [ + 'isValid' => !$this->hasErrors(), + 'errors' => $this->errors, + 'validatedData' => $this->validatedData, + ]; + } +} diff --git a/src/Validator.php b/src/Validator.php index 39e15fc..5eb8285 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -9,11 +9,10 @@ use KaririCode\ProcessorPipeline\ProcessorBuilder; use KaririCode\PropertyInspector\AttributeAnalyzer; use KaririCode\PropertyInspector\AttributeHandler; -use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler; -use KaririCode\PropertyInspector\Contract\PropertyChangeApplier; -use KaririCode\PropertyInspector\Exception\PropertyInspectionException; use KaririCode\PropertyInspector\Utility\PropertyInspector; use KaririCode\Validator\Attribute\Validate; +use KaririCode\Validator\Contract\ValidationResultProcessor; +use KaririCode\Validator\Processor\DefaultValidationResultProcessor; class Validator implements ValidatorContract { @@ -21,10 +20,12 @@ class Validator implements ValidatorContract private ProcessorBuilder $builder; private PropertyInspector $propertyInspector; - private PropertyAttributeHandler&PropertyChangeApplier $attributeHandler; + private AttributeHandler $attributeHandler; - public function __construct(private readonly ProcessorRegistry $registry) - { + public function __construct( + private readonly ProcessorRegistry $registry, + private readonly ValidationResultProcessor $resultProcessor = new DefaultValidationResultProcessor() + ) { $this->builder = new ProcessorBuilder($this->registry); $this->attributeHandler = new AttributeHandler(self::IDENTIFIER, $this->builder); $this->propertyInspector = new PropertyInspector( @@ -32,24 +33,10 @@ public function __construct(private readonly ProcessorRegistry $registry) ); } - public function validate(mixed $object): array + public function validate(mixed $object): ValidationResult { - try { - var_dump($object, $this->attributeHandler); - $validationResults = $this->propertyInspector->inspect($object, $this->attributeHandler); - - $errors = []; - foreach ($validationResults as $property => $result) { - if (false === $result) { - $errors[$property] = ["Validation failed for {$property}"]; - } elseif (is_string($result)) { - $errors[$property] = [$result]; - } - } - - return $errors; - } catch (PropertyInspectionException $e) { - return ['__exception' => [$e->getMessage()]]; - } + $handler = $this->propertyInspector->inspect($object, $this->attributeHandler); + + return $this->resultProcessor->process($handler); } } diff --git a/tests/application.php b/tests/application.php index 91e9c94..90472d1 100644 --- a/tests/application.php +++ b/tests/application.php @@ -6,42 +6,91 @@ use KaririCode\ProcessorPipeline\ProcessorRegistry; use KaririCode\Validator\Attribute\Validate; +use KaririCode\Validator\Contract\ValidationResult; +use KaririCode\Validator\Processor\Date\DateFormatValidator; use KaririCode\Validator\Processor\Input\EmailValidator; -use KaririCode\Validator\Processor\Input\UrlValidator; use KaririCode\Validator\Processor\Input\LengthValidator; -use KaririCode\Validator\Processor\Numeric\RangeValidator; -use KaririCode\Validator\Processor\Numeric\IntegerValidator; -use KaririCode\Validator\Processor\Date\DateFormatValidator; -use KaririCode\Validator\Processor\Date\DateRangeValidator; -use KaririCode\Validator\Processor\Logic\RequiredValidator; +use KaririCode\Validator\Processor\Input\UrlValidator; use KaririCode\Validator\Processor\Logic\ConditionalValidator; +use KaririCode\Validator\Processor\Logic\RequiredValidator; +use KaririCode\Validator\Processor\Numeric\IntegerValidator; +use KaririCode\Validator\Processor\Numeric\RangeValidator; use KaririCode\Validator\Validator; -class UserProfile +class UserRegistration { - #[Validate(validators: ['required', 'length' => ['minLength' => 2, 'maxLength' => 50]])] - private string $name = ''; - - #[Validate(validators: ['required', 'email'])] + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + messages: [ + 'required' => 'Username is required', + 'length' => 'Username must be between 3 and 20 characters', + ] + )] + private string $username = ''; + + #[Validate( + processors: ['required', 'email'], + messages: [ + 'required' => 'Email is required', + 'email' => 'Invalid email format', + ] + )] private string $email = ''; - #[Validate(validators: ['required', 'integer', 'range' => ['min' => 0, 'max' => 120]])] + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 8], + ], + messages: [ + 'required' => 'Password is required', + 'length' => 'Password must be at least 8 characters long', + ] + )] + private string $password = ''; + + #[Validate( + processors: [ + 'required', + 'integer', + 'range' => ['min' => 18, 'max' => 120], + ], + messages: [ + 'required' => 'Age is required', + 'integer' => 'Age must be a whole number', + 'range' => 'Age must be between 18 and 120', + ] + )] private int $age = 0; - #[Validate(validators: ['url'])] + #[Validate( + processors: ['url'], + messages: [ + 'url' => 'Invalid website URL', + ] + )] private string $website = ''; - #[Validate(validators: ['conditional_phone'])] - private string $phoneNumber = ''; + #[Validate( + processors: ['dateFormat' => ['format' => 'Y-m-d']], + messages: [ + 'dateFormat' => 'Invalid date format. Use YYYY-MM-DD', + ] + )] + private string $birthDate = ''; - public function getName(): string + // Getters and setters + public function getUsername(): string { - return $this->name; + return $this->username; } - public function setName(string $name): void + public function setUsername(string $username): void { - $this->name = $name; + $this->username = $username; } public function getEmail(): string @@ -54,6 +103,16 @@ public function setEmail(string $email): void $this->email = $email; } + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): void + { + $this->password = $password; + } + public function getAge(): int { return $this->age; @@ -74,58 +133,111 @@ public function setWebsite(string $website): void $this->website = $website; } - public function getPhoneNumber(): string + public function getBirthDate(): string { - return $this->phoneNumber; + return $this->birthDate; } - public function setPhoneNumber(string $phoneNumber): void + public function setBirthDate(string $birthDate): void { - $this->phoneNumber = $phoneNumber; - } -} - -class Event -{ - #[Validate(validators: ['required', 'date_format' => ['format' => 'Y-m-d']])] - private string $eventDate = ''; - - #[Validate(validators: ['date_range' => ['minDate' => '2023-01-01', 'maxDate' => '2023-12-31', 'format' => 'Y-m-d']])] - private string $registrationDeadline = ''; - - public function getEventDate(): string - { - return $this->eventDate; - } - - public function setEventDate(string $eventDate): void - { - $this->eventDate = $eventDate; - } - - public function getRegistrationDeadline(): string - { - return $this->registrationDeadline; - } - - public function setRegistrationDeadline(string $registrationDeadline): void - { - $this->registrationDeadline = $registrationDeadline; + $this->birthDate = $birthDate; } } +// Set up the validator $registry = new ProcessorRegistry(); $registry->register('validator', 'required', new RequiredValidator()); -$registry->register('validator', 'date_format', new DateFormatValidator()); +$registry->register('validator', 'email', new EmailValidator()); +$registry->register('validator', 'length', new LengthValidator()); +$registry->register('validator', 'integer', new IntegerValidator()); +$registry->register('validator', 'range', new RangeValidator()); +$registry->register('validator', 'url', new UrlValidator()); +$registry->register('validator', 'dateFormat', new DateFormatValidator()); +$registry->register('validator', 'conditional', new ConditionalValidator()); $validator = new Validator($registry); -$event = new Event(); -$event->setEventDate('2023-13-01'); -$event->setRegistrationDeadline('2024-01-01'); - +// Scenario 1: All validations pass +echo "Scenario 1: All validations pass\n"; +$validUser = new UserRegistration(); +$validUser->setUsername('walmir_silva'); +$validUser->setEmail('walmir.silva@example.com'); +$validUser->setPassword('str0ngP@ssw0rd'); +$validUser->setAge(35); +$validUser->setWebsite('https://walmirsilva.com'); +$validUser->setBirthDate('1988-05-15'); + +$validationResult1 = $validator->validate($validUser); +displayValidationResult($validationResult1); + +// Scenario 2: Multiple validation errors +echo "Scenario 2: Multiple validation errors\n"; +$invalidUser = new UserRegistration(); +$invalidUser->setUsername('w'); // Too short +$invalidUser->setEmail('invalid-email'); // Invalid email format +$invalidUser->setPassword('weak'); // Too short +$invalidUser->setAge(15); // Below minimum age +$invalidUser->setWebsite('not-a-url'); // Invalid URL +$invalidUser->setBirthDate('15-05-1988'); // Incorrect date format + +$validationResult2 = $validator->validate($invalidUser); +displayValidationResult($validationResult2); + +// Scenario 3: Some fields valid, some invalid +echo "Scenario 3: Some fields valid, some invalid\n"; +$partiallyValidUser = new UserRegistration(); +$partiallyValidUser->setUsername('walmir_silva'); +$partiallyValidUser->setEmail('walmir.silva@example.com'); +$partiallyValidUser->setPassword('short'); // Too short +$partiallyValidUser->setAge(150); // Above maximum age +$partiallyValidUser->setWebsite('https://walmirsilva.com'); +$partiallyValidUser->setBirthDate('1988-05-15'); + +$validationResult3 = $validator->validate($partiallyValidUser); +displayValidationResult($validationResult3); + +// Scenario 4: Empty fields (testing required validator) +echo "Scenario 4: Empty fields (testing required validator)\n"; +$emptyFieldsUser = new UserRegistration(); +// Not setting any fields, leaving them as default empty values + +$validationResult4 = $validator->validate($emptyFieldsUser); +displayValidationResult($validationResult4); + +// Example of using validated data (for the valid case) +if (!$validationResult1->hasErrors()) { + $validatedData = $validationResult1->getValidatedData(); + echo "Using validated data:\n"; + echo "Creating user account for: {$validatedData['username']}\n"; + echo "Sending welcome email to: {$validatedData['email']}\n"; + echo "User's age: {$validatedData['age']}\n"; + echo "Website: {$validatedData['website']}\n"; + echo "Birth Date: {$validatedData['birthDate']}\n"; +} -$validationResults = $validator->validate($event); +// Helper function to display validation results +function displayValidationResult(ValidationResult $result): void +{ + $displayedErrors = []; + if ($result->hasErrors()) { + echo "Validation failed. Errors:\n"; + + foreach ($result->getErrors() as $property => $errors) { + foreach ($errors as $error) { + $errorKey = $property . '-' . $error['errorKey']; + if (!in_array($errorKey, $displayedErrors, true)) { + echo "- $property: {$error['message']} (Error Key: {$error['errorKey']})\n"; + $displayedErrors[] = $errorKey; + } + } + } + } else { + echo "Validation passed successfully.\n"; + } -// var_dump($validationResults); -// var_dump($event); + echo "\nValidated Data:\n"; + foreach ($result->getValidatedData() as $property => $value) { + echo "- $property: " . (is_scalar($value) ? $value : gettype($value)) . "\n"; + } + echo "\n"; +} From 0e9c74dfabf3b2ec932993092f513c82e304a8da Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 15:24:17 -0300 Subject: [PATCH 04/12] feat: add unit tests for Validate attribute - Added tests to ensure Validate implements ProcessableAttribute and CustomizableMessageAttribute interfaces. - Verified that Validate can handle single and multiple processors, including processors with configurations. - Implemented tests for filtering invalid processors. - Added message handling tests to validate messages associated with specific processors. - Ensured compatibility with both configured and non-configured processors. --- tests/Attribute/ValidateTest.php | 139 +++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/Attribute/ValidateTest.php diff --git a/tests/Attribute/ValidateTest.php b/tests/Attribute/ValidateTest.php new file mode 100644 index 0000000..5109729 --- /dev/null +++ b/tests/Attribute/ValidateTest.php @@ -0,0 +1,139 @@ +assertInstanceOf(ProcessableAttribute::class, $validate); + } + + public function testValidateImplementsCustomizableMessageAttribute(): void + { + $validate = new Validate([]); + $this->assertInstanceOf(CustomizableMessageAttribute::class, $validate); + } + + public function testValidateIsAttribute(): void + { + $reflectionClass = new \ReflectionClass(Validate::class); + $attributes = $reflectionClass->getAttributes(); + + $this->assertCount(1, $attributes); + $this->assertSame(\Attribute::class, $attributes[0]->getName()); + $this->assertSame([\Attribute::TARGET_PROPERTY], $attributes[0]->getArguments()); + } + + /** + * @dataProvider validProcessorsProvider + */ + public function testConstructorWithValidProcessors(array $processors, array $expected): void + { + $validate = new Validate($processors); + $this->assertEquals($expected, $validate->getProcessors()); + } + + public function testConstructorFiltersInvalidProcessors(): void + { + $processors = ['required', null, false, 'email']; + $expectedProcessors = ['required', 'email']; + $validate = new Validate($processors); + + $this->assertEquals($expectedProcessors, $validate->getProcessors()); + } + + public function testConstructorWithEmptyProcessors(): void + { + $validate = new Validate([]); + $this->assertEmpty($validate->getProcessors()); + } + + /** + * @dataProvider messageProvider + */ + public function testGetMessage(array $processors, array $messages, string $processor, ?string $expected): void + { + $validate = new Validate($processors, $messages); + $this->assertSame($expected, $validate->getMessage($processor)); + } + + public function testGetProcessorsReturnsProcessors(): void + { + $processors = ['required', 'email']; + $validate = new Validate($processors); + + $this->assertEquals($processors, $validate->getProcessors()); + } + + public static function validProcessorsProvider(): array + { + return [ + 'single processor' => [ + ['required'], + ['required'], + ], + 'multiple processors' => [ + ['required', 'email', 'length'], + ['required', 'email', 'length'], + ], + 'processor with config' => [ + [ + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + ['length'], + ], + 'mixed processors' => [ + [ + 'required', + 'email' => ['message' => 'Invalid email'], + ], + ['required', 'email'], + ], + ]; + } + + public static function messageProvider(): array + { + return [ + 'existing message' => [ + ['required'], + ['required' => 'Field is required'], + 'required', + 'Field is required', + ], + 'non-existing message' => [ + ['required'], + ['required' => 'Field is required'], + 'email', + null, + ], + 'empty messages' => [ + ['required'], + [], + 'required', + null, + ], + 'message for non-registered processor' => [ + ['required'], + ['email' => 'Invalid email'], + 'required', + null, + ], + 'null message' => [ + ['required'], + ['required' => null], + 'required', + null, + ], + ]; + } +} From fd86a85ae6de269d6abca12fd879b020c4dfd5ed Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 15:29:37 -0300 Subject: [PATCH 05/12] feat: add unit tests for DateFormatValidator, EmailValidator, LengthValidator, and UrlValidator - Added unit tests for DateFormatValidator to validate default and custom date formats, along with invalid formats and data types. - Added unit tests for EmailValidator to validate correct email formats, handle invalid formats, and manage incorrect data types. - Implemented tests for LengthValidator to handle valid lengths, input that is too short or too long, and incorrect data types. Also covered missing or incomplete configuration cases. - Added unit tests for UrlValidator to check valid and invalid URL formats, as well as invalid data types. --- .../Date/DateFormatValidatorTest.php | 56 +++++++++++++ tests/Processor/Input/EmailValidatorTest.php | 69 ++++++++++++++++ tests/Processor/Input/LengthValidatorTest.php | 80 +++++++++++++++++++ tests/Processor/Input/UrlValidatorTest.php | 70 ++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 tests/Processor/Date/DateFormatValidatorTest.php create mode 100644 tests/Processor/Input/EmailValidatorTest.php create mode 100644 tests/Processor/Input/LengthValidatorTest.php create mode 100644 tests/Processor/Input/UrlValidatorTest.php diff --git a/tests/Processor/Date/DateFormatValidatorTest.php b/tests/Processor/Date/DateFormatValidatorTest.php new file mode 100644 index 0000000..38aecc6 --- /dev/null +++ b/tests/Processor/Date/DateFormatValidatorTest.php @@ -0,0 +1,56 @@ +validator = new DateFormatValidator(); + } + + public function testDefaultFormatValidation(): void + { + $input = '2024-03-15'; + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testCustomFormatValidation(): void + { + $this->validator->configure(['format' => 'd/m/Y']); + $input = '15/03/2024'; + + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testInvalidFormat(): void + { + $input = '15-03-2024'; + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidFormat', $this->validator->getErrorKey()); + } + + public function testInvalidType(): void + { + $input = 123; + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidType', $this->validator->getErrorKey()); + } +} diff --git a/tests/Processor/Input/EmailValidatorTest.php b/tests/Processor/Input/EmailValidatorTest.php new file mode 100644 index 0000000..f413dbf --- /dev/null +++ b/tests/Processor/Input/EmailValidatorTest.php @@ -0,0 +1,69 @@ +validator = new EmailValidator(); + } + + /** + * @dataProvider validEmailProvider + */ + public function testValidEmails(string $email): void + { + $this->validator->process($email); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + /** + * @dataProvider invalidEmailProvider + */ + public function testInvalidEmails(string $email): void + { + $this->validator->process($email); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidFormat', $this->validator->getErrorKey()); + } + + public function testInvalidType(): void + { + $this->validator->process(123); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidType', $this->validator->getErrorKey()); + } + + public static function validEmailProvider(): array + { + return [ + ['test@example.com'], + ['user.name@domain.com'], + ['user+tag@domain.com'], + ['user@subdomain.domain.com'], + ]; + } + + public static function invalidEmailProvider(): array + { + return [ + ['test@'], + ['@domain.com'], + ['test@domain'], + ['test.domain.com'], + ['test@domain..com'], + ]; + } +} diff --git a/tests/Processor/Input/LengthValidatorTest.php b/tests/Processor/Input/LengthValidatorTest.php new file mode 100644 index 0000000..f4beefd --- /dev/null +++ b/tests/Processor/Input/LengthValidatorTest.php @@ -0,0 +1,80 @@ +validator = new LengthValidator(); + } + + public function testValidLength(): void + { + $this->validator->configure(['minLength' => 3, 'maxLength' => 10]); + $input = 'test'; + + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testTooShort(): void + { + $this->validator->configure(['minLength' => 5, 'maxLength' => 10]); + $input = 'test'; + + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('tooShort', $this->validator->getErrorKey()); + } + + public function testTooLong(): void + { + $this->validator->configure(['minLength' => 2, 'maxLength' => 5]); + $input = 'testing'; + + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('tooLong', $this->validator->getErrorKey()); + } + + public function testInvalidType(): void + { + $this->validator->configure(['minLength' => 2, 'maxLength' => 5]); + + $this->validator->process(123); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidType', $this->validator->getErrorKey()); + } + + public function testEmptyConfigThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure([]); + } + + public function testMissingMinLengthThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure(['maxLength' => 5]); + } + + public function testMissingMaxLengthThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure(['minLength' => 2]); + } +} diff --git a/tests/Processor/Input/UrlValidatorTest.php b/tests/Processor/Input/UrlValidatorTest.php new file mode 100644 index 0000000..8cee18e --- /dev/null +++ b/tests/Processor/Input/UrlValidatorTest.php @@ -0,0 +1,70 @@ +validator = new UrlValidator(); + } + + /** + * @dataProvider validUrlProvider + */ + public function testValidUrls(string $url): void + { + $this->validator->process($url); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + /** + * @dataProvider invalidUrlProvider + */ + public function testInvalidUrls(string $url): void + { + $this->validator->process($url); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidFormat', $this->validator->getErrorKey()); + } + + public function testInvalidType(): void + { + $this->validator->process(123); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidType', $this->validator->getErrorKey()); + } + + public static function validUrlProvider(): array + { + return [ + ['https://example.com'], + ['http://subdomain.example.com'], + ['https://example.com/path'], + ['http://example.com:8080'], + ['https://example.com/path?param=value'], + ]; + } + + public static function invalidUrlProvider(): array + { + return [ + ['example.com'], + ['not a url'], + ['http://'], + ['https://'], + ['ftp:/example.com'], + ]; + } +} From 1f1cf37cb030668171a1382eba0e207338d55e8f Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 15:33:42 -0300 Subject: [PATCH 06/12] feat: add unit tests for ConditionalValidator and RequiredValidator - Added tests for ConditionalValidator to validate custom conditions and associated validation logic. - Included tests for cases where the condition is met or not met, and how it handles valid and invalid input. - Added unit tests for RequiredValidator to ensure that fields are correctly marked as required. - Covered cases for empty values, non-empty values, and handling different data types. --- .../Logic/ConditionalValidatorTest.php | 73 +++++++++++++++++++ .../Processor/Logic/RequiredValidatorTest.php | 62 ++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/Processor/Logic/ConditionalValidatorTest.php create mode 100644 tests/Processor/Logic/RequiredValidatorTest.php diff --git a/tests/Processor/Logic/ConditionalValidatorTest.php b/tests/Processor/Logic/ConditionalValidatorTest.php new file mode 100644 index 0000000..7753f19 --- /dev/null +++ b/tests/Processor/Logic/ConditionalValidatorTest.php @@ -0,0 +1,73 @@ +validator = new ConditionalValidator(); + } + + public function testValidCondition(): void + { + $this->validator->configure([ + 'condition' => fn ($input) => $input > 0, + 'validator' => fn ($input) => $input < 10 + ]); + + $this->validator->process(5); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testInvalidCondition(): void + { + $this->validator->configure([ + 'condition' => fn ($input) => $input > 0, + 'validator' => fn ($input) => $input < 10 + ]); + + $this->validator->process(15); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('conditionNotMet', $this->validator->getErrorKey()); + } + + public function testConditionNotMet(): void + { + $this->validator->configure([ + 'condition' => fn ($input) => $input > 100, + 'validator' => fn ($input) => $input < 10 + ]); + + $this->validator->process(50); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testMissingConditionThrowsException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->validator->configure([ + 'validator' => fn ($input) => true + ]); + } + + public function testMissingValidatorThrowsException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->validator->configure([ + 'condition' => fn ($input) => true + ]); + } +} diff --git a/tests/Processor/Logic/RequiredValidatorTest.php b/tests/Processor/Logic/RequiredValidatorTest.php new file mode 100644 index 0000000..baf99e3 --- /dev/null +++ b/tests/Processor/Logic/RequiredValidatorTest.php @@ -0,0 +1,62 @@ +validator = new RequiredValidator(); + } + + /** + * @dataProvider validValuesProvider + */ + public function testValidValues(mixed $value): void + { + $this->validator->process($value); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testEmptyValues(mixed $value): void + { + $this->validator->process($value); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('missingValue', $this->validator->getErrorKey()); + } + + public static function validValuesProvider(): array + { + return [ + ['test'], + [123], + [0], + [false], + [['item']], + [' test '], + ]; + } + + public static function emptyValuesProvider(): array + { + return [ + [null], + [''], + [' '], + [[]], + ]; + } +} From ea6735d959dd2d49d9eec6e6b8e60f872a722aab Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 15:43:08 -0300 Subject: [PATCH 07/12] feat: optimize IntegerValidator and add tests for validation logic - Optimized IntegerValidator by refactoring the isValidInteger method to improve readability and efficiency. - Replaced multiple if statements with a single return expression for cleaner and more concise code. - Added IntegerValidatorTest to ensure correct handling of valid and invalid integers, including negative values and edge cases. - Updated ConditionalValidatorTest to ensure consistent testing for conditional logic. --- src/Processor/Numeric/IntegerValidator.php | 7 +- .../Logic/ConditionalValidatorTest.php | 10 +-- .../Numeric/IntegerValidatorTest.php | 66 +++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 tests/Processor/Numeric/IntegerValidatorTest.php diff --git a/src/Processor/Numeric/IntegerValidator.php b/src/Processor/Numeric/IntegerValidator.php index c7c4675..2ab6c1d 100644 --- a/src/Processor/Numeric/IntegerValidator.php +++ b/src/Processor/Numeric/IntegerValidator.php @@ -10,10 +10,15 @@ class IntegerValidator extends AbstractValidatorProcessor { public function process(mixed $input): mixed { - if (!is_int($input) && (!is_string($input) || !ctype_digit($input))) { + if (!$this->isValidInteger($input)) { $this->setInvalid('notAnInteger'); } return $input; } + + private function isValidInteger(mixed $input): bool + { + return is_int($input) || (is_string($input) && false !== filter_var($input, FILTER_VALIDATE_INT)); + } } diff --git a/tests/Processor/Logic/ConditionalValidatorTest.php b/tests/Processor/Logic/ConditionalValidatorTest.php index 7753f19..92e8ee2 100644 --- a/tests/Processor/Logic/ConditionalValidatorTest.php +++ b/tests/Processor/Logic/ConditionalValidatorTest.php @@ -20,7 +20,7 @@ public function testValidCondition(): void { $this->validator->configure([ 'condition' => fn ($input) => $input > 0, - 'validator' => fn ($input) => $input < 10 + 'validator' => fn ($input) => $input < 10, ]); $this->validator->process(5); @@ -33,7 +33,7 @@ public function testInvalidCondition(): void { $this->validator->configure([ 'condition' => fn ($input) => $input > 0, - 'validator' => fn ($input) => $input < 10 + 'validator' => fn ($input) => $input < 10, ]); $this->validator->process(15); @@ -46,7 +46,7 @@ public function testConditionNotMet(): void { $this->validator->configure([ 'condition' => fn ($input) => $input > 100, - 'validator' => fn ($input) => $input < 10 + 'validator' => fn ($input) => $input < 10, ]); $this->validator->process(50); @@ -59,7 +59,7 @@ public function testMissingConditionThrowsException(): void { $this->expectException(\InvalidArgumentException::class); $this->validator->configure([ - 'validator' => fn ($input) => true + 'validator' => fn ($input) => true, ]); } @@ -67,7 +67,7 @@ public function testMissingValidatorThrowsException(): void { $this->expectException(\InvalidArgumentException::class); $this->validator->configure([ - 'condition' => fn ($input) => true + 'condition' => fn ($input) => true, ]); } } diff --git a/tests/Processor/Numeric/IntegerValidatorTest.php b/tests/Processor/Numeric/IntegerValidatorTest.php new file mode 100644 index 0000000..75621eb --- /dev/null +++ b/tests/Processor/Numeric/IntegerValidatorTest.php @@ -0,0 +1,66 @@ +validator = new IntegerValidator(); + } + + /** + * @dataProvider validIntegerProvider + */ + public function testValidIntegers(mixed $value): void + { + $this->validator->process($value); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + /** + * @dataProvider invalidIntegerProvider + */ + public function testInvalidIntegers(mixed $value): void + { + $this->validator->process($value); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('notAnInteger', $this->validator->getErrorKey()); + } + + public static function validIntegerProvider(): array + { + return [ + [42], + ['42'], + [0], + ['0'], + [-123], + ['-123'], + ]; + } + + public static function invalidIntegerProvider(): array + { + return [ + [3.14], + ['3.14'], + ['abc'], + ['12.34'], + [null], + [[]], + [false], + [''], + ]; + } +} From 212d12ccd5e5a792bb9cb3874fc169c5cecd64ac Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 16:09:52 -0300 Subject: [PATCH 08/12] feat: add DateValidatorException and refactor DateRangeValidator with custom exceptions - Added DateValidatorException to handle invalid date format errors. - Refactored DateRangeValidator to use DateValidatorException and MissingProcessorConfigException for more consistent error handling. - Improved date parsing logic in DateRangeValidator to throw specific exceptions for missing configurations and invalid date formats. - Added unit tests for DateRangeValidator, covering valid and invalid date ranges, custom date formats, and error handling for missing configurations. - Added RangeValidatorTest to validate correct behavior of range validation for numeric values. --- src/Exception/DateValidatorException.php | 24 ++++ src/Processor/Date/DateRangeValidator.php | 30 +++-- .../Processor/Date/DateRangeValidatorTest.php | 123 ++++++++++++++++++ .../Processor/Numeric/RangeValidatorTest.php | 83 ++++++++++++ 4 files changed, 248 insertions(+), 12 deletions(-) create mode 100644 src/Exception/DateValidatorException.php create mode 100644 tests/Processor/Date/DateRangeValidatorTest.php create mode 100644 tests/Processor/Numeric/RangeValidatorTest.php diff --git a/src/Exception/DateValidatorException.php b/src/Exception/DateValidatorException.php new file mode 100644 index 0000000..6a47102 --- /dev/null +++ b/src/Exception/DateValidatorException.php @@ -0,0 +1,24 @@ +minDate = $this->parseDate($options['minDate']); - $this->maxDate = $this->parseDate($options['maxDate']); - - if ($this->minDate > $this->maxDate) { - throw new \InvalidArgumentException('minDate must be less than or equal to maxDate'); + if (!isset($options['maxDate'])) { + throw MissingProcessorConfigException::missingConfiguration('DateRangeValidator', 'maxDate'); } if (isset($options['format'])) { $this->format = $options['format']; } + + $this->minDate = self::parseDate($options['minDate'], $this->format); + $this->maxDate = self::parseDate($options['maxDate'], $this->format); + + if ($this->minDate > $this->maxDate) { + throw MissingProcessorConfigException::missingConfiguration('DateRangeValidator', 'minDate and maxDate order'); + } } public function process(mixed $input): mixed @@ -53,11 +59,11 @@ public function process(mixed $input): mixed return $input; } - private function parseDate(string $date): \DateTimeInterface + private static function parseDate(string $date, string $format): \DateTimeInterface { - $parsedDate = \DateTime::createFromFormat($this->format, $date); - if (!$parsedDate) { - throw new \InvalidArgumentException("Invalid date format. Expected: {$this->format}"); + $parsedDate = \DateTime::createFromFormat($format, $date); + if (!$parsedDate || $parsedDate->format($format) !== $date) { + throw DateValidatorException::invalidDateFormat($format, $date); } return $parsedDate; diff --git a/tests/Processor/Date/DateRangeValidatorTest.php b/tests/Processor/Date/DateRangeValidatorTest.php new file mode 100644 index 0000000..94b05c6 --- /dev/null +++ b/tests/Processor/Date/DateRangeValidatorTest.php @@ -0,0 +1,123 @@ +validator = new DateRangeValidator(); + } + + public function testValidDateInRange(): void + { + $this->validator->configure([ + 'minDate' => '2024-01-01', + 'maxDate' => '2024-12-31', + ]); + + $this->validator->process('2024-06-15'); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testDateBeforeRange(): void + { + $this->validator->configure([ + 'minDate' => '2024-01-01', + 'maxDate' => '2024-12-31', + ]); + + $this->validator->process('2023-12-31'); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('outOfRange', $this->validator->getErrorKey()); + } + + public function testDateAfterRange(): void + { + $this->validator->configure([ + 'minDate' => '2024-01-01', + 'maxDate' => '2024-12-31', + ]); + + $this->validator->process('2025-01-01'); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('outOfRange', $this->validator->getErrorKey()); + } + + public function testInvalidDateFormat(): void + { + $this->validator->configure([ + 'minDate' => '2024-01-01', + 'maxDate' => '2024-12-31', + ]); + + $this->validator->process('2024/06/15'); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidDate', $this->validator->getErrorKey()); + } + + public function testInvalidType(): void + { + $this->validator->configure([ + 'minDate' => '2024-01-01', + 'maxDate' => '2024-12-31', + ]); + + $this->validator->process(123); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('invalidType', $this->validator->getErrorKey()); + } + + public function testMissingMinDateThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure([ + 'maxDate' => '2024-12-31', + ]); + } + + public function testMissingMaxDateThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure([ + 'minDate' => '2024-01-01', + ]); + } + + public function testInvalidMinMaxOrderThrowsException(): void + { + $this->expectException(MissingProcessorConfigException::class); + $this->validator->configure([ + 'minDate' => '2024-12-31', + 'maxDate' => '2024-01-01', + ]); + } + + public function testCustomDateFormat(): void + { + $this->validator->configure([ + 'minDate' => '01/01/2024', + 'maxDate' => '31/12/2024', + 'format' => 'd/m/Y', + ]); + + $this->validator->process('15/06/2024'); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } +} diff --git a/tests/Processor/Numeric/RangeValidatorTest.php b/tests/Processor/Numeric/RangeValidatorTest.php new file mode 100644 index 0000000..ac1f948 --- /dev/null +++ b/tests/Processor/Numeric/RangeValidatorTest.php @@ -0,0 +1,83 @@ +validator = new RangeValidator(); + } + + public function testDefaultRange(): void + { + $this->validator->configure([]); + $input = 42; + + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testCustomRange(): void + { + $this->validator->configure(['min' => 1, 'max' => 100]); + $input = 42; + + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } + + public function testBelowRange(): void + { + $this->validator->configure(['min' => 10, 'max' => 20]); + $input = 5; + + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('outOfRange', $this->validator->getErrorKey()); + } + + public function testAboveRange(): void + { + $this->validator->configure(['min' => 10, 'max' => 20]); + $input = 25; + + $this->validator->process($input); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('outOfRange', $this->validator->getErrorKey()); + } + + public function testNonNumericInput(): void + { + $this->validator->configure(['min' => 1, 'max' => 100]); + + $this->validator->process('abc'); + + $this->assertFalse($this->validator->isValid()); + $this->assertSame('notNumeric', $this->validator->getErrorKey()); + } + + public function testStringNumericInput(): void + { + $this->validator->configure(['min' => 1, 'max' => 100]); + $input = '42'; + + $this->validator->process($input); + + $this->assertTrue($this->validator->isValid()); + $this->assertEmpty($this->validator->getErrorKey()); + } +} From 0c264b950026919b5862e0d719811dd5f3604a98 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 17:57:27 -0300 Subject: [PATCH 09/12] feat(tests): add validator unit tests This commit adds comprehensive test coverage for the validator module: - DefaultValidationResultProcessorTest: Tests validation result processing including: - Valid data processing - Error handling - Multiple errors for same property - Empty property handling - Mixed valid/invalid property cases - ValidationResultTest: Tests validation result management including: - Error addition and retrieval - Validated data storage - State management - Data type handling - Array serialization - Duplicate error prevention - ValidatorTest: Tests core validator functionality including: - Object validation - Attribute processing - Error collection - Result generation - Integration with processors --- .../DefaultValidationResultProcessorTest.php | 196 +++++++++++++++++ tests/ValidationResultTest.php | 207 ++++++++++++++++++ tests/ValidatorTest.php | 165 ++++++++++++++ 3 files changed, 568 insertions(+) create mode 100644 tests/Processor/DefaultValidationResultProcessorTest.php create mode 100644 tests/ValidationResultTest.php create mode 100644 tests/ValidatorTest.php diff --git a/tests/Processor/DefaultValidationResultProcessorTest.php b/tests/Processor/DefaultValidationResultProcessorTest.php new file mode 100644 index 0000000..5fbc3ef --- /dev/null +++ b/tests/Processor/DefaultValidationResultProcessorTest.php @@ -0,0 +1,196 @@ +attributeHandler = $this->createMock(AttributeHandler::class); + $this->validationResult = new ValidationResult(); + $this->processor = new DefaultValidationResultProcessor($this->validationResult); + } + + public function testProcessWithValidData(): void + { + $processedValues = [ + 'name' => ['value' => 'Walmir Silva'], + 'email' => ['value' => 'walmir@example.com'], + ]; + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessedPropertyValues') + ->willReturn($processedValues); + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessingResultErrors') + ->willReturn([]); + + $result = $this->processor->process($this->attributeHandler); + + $this->assertInstanceOf(ValidationResult::class, $result); + $this->assertEquals( + [ + 'name' => 'Walmir Silva', + 'email' => 'walmir@example.com', + ], + $result->getValidatedData() + ); + $this->assertFalse($result->hasErrors()); + } + + public function testProcessWithErrors(): void + { + $processedValues = [ + 'email' => ['value' => 'invalid-email'], + ]; + + $errors = [ + 'email' => [ + [ + 'errorKey' => 'invalidFormat', + 'message' => 'Invalid email format', + ], + ], + ]; + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessedPropertyValues') + ->willReturn($processedValues); + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessingResultErrors') + ->willReturn($errors); + + $result = $this->processor->process($this->attributeHandler); + + $this->assertInstanceOf(ValidationResult::class, $result); + $this->assertEquals(['email' => 'invalid-email'], $result->getValidatedData()); + $this->assertTrue($result->hasErrors()); + $this->assertArrayHasKey('email', $result->getErrors()); + $this->assertEquals('invalidFormat', $result->getErrors()['email'][0]['errorKey']); + $this->assertEquals('Invalid email format', $result->getErrors()['email'][0]['message']); + } + + public function testProcessWithMultipleErrorsForSameProperty(): void + { + $processedValues = [ + 'password' => ['value' => 'weak'], + ]; + + $errors = [ + 'password' => [ + [ + 'errorKey' => 'tooShort', + 'message' => 'Password is too short', + ], + [ + 'errorKey' => 'complexity', + 'message' => 'Password needs special characters', + ], + ], + ]; + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessedPropertyValues') + ->willReturn($processedValues); + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessingResultErrors') + ->willReturn($errors); + + $result = $this->processor->process($this->attributeHandler); + + $this->assertInstanceOf(ValidationResult::class, $result); + $this->assertEquals(['password' => 'weak'], $result->getValidatedData()); + $this->assertTrue($result->hasErrors()); + + $resultErrors = $result->getErrors()['password']; + $this->assertCount(2, $resultErrors); + $this->assertEquals('tooShort', $resultErrors[0]['errorKey']); + $this->assertEquals('Password is too short', $resultErrors[0]['message']); + $this->assertEquals('complexity', $resultErrors[1]['errorKey']); + $this->assertEquals('Password needs special characters', $resultErrors[1]['message']); + } + + public function testProcessWithNoProperties(): void + { + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessedPropertyValues') + ->willReturn([]); + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessingResultErrors') + ->willReturn([]); + + $result = $this->processor->process($this->attributeHandler); + + $this->assertInstanceOf(ValidationResult::class, $result); + $this->assertEmpty($result->getValidatedData()); + $this->assertFalse($result->hasErrors()); + } + + public function testProcessWithMixedValidAndInvalidProperties(): void + { + $processedValues = [ + 'name' => ['value' => 'Walmir Silva'], + 'email' => ['value' => 'invalid-email'], + 'age' => ['value' => 25], + ]; + + $errors = [ + 'email' => [ + [ + 'errorKey' => 'invalidFormat', + 'message' => 'Invalid email format', + ], + ], + ]; + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessedPropertyValues') + ->willReturn($processedValues); + + $this->attributeHandler + ->expects($this->once()) + ->method('getProcessingResultErrors') + ->willReturn($errors); + + $result = $this->processor->process($this->attributeHandler); + + $this->assertInstanceOf(ValidationResult::class, $result); + $this->assertEquals( + [ + 'name' => 'Walmir Silva', + 'email' => 'invalid-email', + 'age' => 25, + ], + $result->getValidatedData() + ); + $this->assertTrue($result->hasErrors()); + $this->assertArrayHasKey('email', $result->getErrors()); + $this->assertEquals('invalidFormat', $result->getErrors()['email'][0]['errorKey']); + $this->assertEquals('Invalid email format', $result->getErrors()['email'][0]['message']); + } +} diff --git a/tests/ValidationResultTest.php b/tests/ValidationResultTest.php new file mode 100644 index 0000000..dbe01a3 --- /dev/null +++ b/tests/ValidationResultTest.php @@ -0,0 +1,207 @@ +validationResult = new ValidationResult(); + } + + public function testInitialState(): void + { + $this->assertFalse($this->validationResult->hasErrors()); + $this->assertEmpty($this->validationResult->getErrors()); + $this->assertEmpty($this->validationResult->getValidatedData()); + + $expectedArray = [ + 'isValid' => true, + 'errors' => [], + 'validatedData' => [], + ]; + $this->assertEquals($expectedArray, $this->validationResult->toArray()); + } + + public function testAddError(): void + { + $this->validationResult->addError('email', 'invalidFormat', 'Invalid email format'); + + $this->assertTrue($this->validationResult->hasErrors()); + + $expectedErrors = [ + 'email' => [ + [ + 'errorKey' => 'invalidFormat', + 'message' => 'Invalid email format', + ], + ], + ]; + $this->assertEquals($expectedErrors, $this->validationResult->getErrors()); + } + + public function testAddMultipleErrorsForSameProperty(): void + { + $this->validationResult->addError('password', 'tooShort', 'Password is too short'); + $this->validationResult->addError('password', 'complexity', 'Password needs special characters'); + + $this->assertTrue($this->validationResult->hasErrors()); + + $expectedErrors = [ + 'password' => [ + [ + 'errorKey' => 'tooShort', + 'message' => 'Password is too short', + ], + [ + 'errorKey' => 'complexity', + 'message' => 'Password needs special characters', + ], + ], + ]; + $this->assertEquals($expectedErrors, $this->validationResult->getErrors()); + } + + public function testAddDuplicateError(): void + { + $this->validationResult->addError('email', 'invalidFormat', 'Invalid email format'); + $this->validationResult->addError('email', 'invalidFormat', 'Invalid email format'); + + $expectedErrors = [ + 'email' => [ + [ + 'errorKey' => 'invalidFormat', + 'message' => 'Invalid email format', + ], + ], + ]; + $this->assertEquals($expectedErrors, $this->validationResult->getErrors()); + $this->assertCount(1, $this->validationResult->getErrors()['email']); + } + + public function testSetValidatedData(): void + { + $this->validationResult->setValidatedData('name', 'Walmir Silva'); + $this->validationResult->setValidatedData('age', 30); + + $expectedData = [ + 'name' => 'Walmir Silva', + 'age' => 30, + ]; + $this->assertEquals($expectedData, $this->validationResult->getValidatedData()); + } + + public function testOverwriteValidatedData(): void + { + $this->validationResult->setValidatedData('age', 30); + $this->validationResult->setValidatedData('age', 31); + + $expectedData = ['age' => 31]; + $this->assertEquals($expectedData, $this->validationResult->getValidatedData()); + } + + public function testToArrayWithValidData(): void + { + $this->validationResult->setValidatedData('name', 'Walmir Silva'); + $this->validationResult->setValidatedData('email', 'walmir@example.com'); + + $expected = [ + 'isValid' => true, + 'errors' => [], + 'validatedData' => [ + 'name' => 'Walmir Silva', + 'email' => 'walmir@example.com', + ], + ]; + + $this->assertEquals($expected, $this->validationResult->toArray()); + } + + public function testToArrayWithErrors(): void + { + $this->validationResult->setValidatedData('email', 'invalid'); + $this->validationResult->addError('email', 'invalidFormat', 'Invalid email format'); + + $expected = [ + 'isValid' => false, + 'errors' => [ + 'email' => [ + [ + 'errorKey' => 'invalidFormat', + 'message' => 'Invalid email format', + ], + ], + ], + 'validatedData' => [ + 'email' => 'invalid', + ], + ]; + + $this->assertEquals($expected, $this->validationResult->toArray()); + } + + public function testSetValidatedDataWithDifferentTypes(): void + { + $testData = [ + 'string' => 'test string', + 'integer' => 42, + 'float' => 3.14, + 'boolean' => true, + 'array' => ['a', 'b', 'c'], + 'null' => null, + 'object' => new \stdClass(), + ]; + + foreach ($testData as $key => $value) { + $this->validationResult->setValidatedData($key, $value); + } + + $validatedData = $this->validationResult->getValidatedData(); + foreach ($testData as $key => $value) { + $this->assertSame($value, $validatedData[$key]); + } + } + + public function testErrorsForMultipleProperties(): void + { + $this->validationResult->addError('username', 'required', 'Username is required'); + $this->validationResult->addError('email', 'invalidFormat', 'Invalid email format'); + $this->validationResult->addError('password', 'tooShort', 'Password is too short'); + + $this->assertTrue($this->validationResult->hasErrors()); + $errors = $this->validationResult->getErrors(); + + $this->assertCount(3, $errors); + $this->assertArrayHasKey('username', $errors); + $this->assertArrayHasKey('email', $errors); + $this->assertArrayHasKey('password', $errors); + } + + public function testMixedValidAndInvalidData(): void + { + $this->validationResult->setValidatedData('name', 'Walmir Silva'); + $this->validationResult->setValidatedData('age', 25); + + $this->validationResult->addError('email', 'required', 'Email is required'); + $this->validationResult->addError('password', 'tooShort', 'Password is too short'); + + $result = $this->validationResult->toArray(); + + $this->assertFalse($result['isValid']); + $this->assertCount(2, $result['errors']); + $this->assertCount(2, $result['validatedData']); + + $this->assertEquals('Walmir Silva', $result['validatedData']['name']); + $this->assertEquals(25, $result['validatedData']['age']); + + $this->assertEquals('Email is required', $result['errors']['email'][0]['message']); + $this->assertEquals('Password is too short', $result['errors']['password'][0]['message']); + } +} diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php new file mode 100644 index 0000000..5ac0876 --- /dev/null +++ b/tests/ValidatorTest.php @@ -0,0 +1,165 @@ +registry = $this->createMock(ProcessorRegistry::class); + $this->resultProcessor = $this->createMock(ValidationResultProcessor::class); + + $this->registry->method('get') + ->willReturnMap([ + ['validator', 'required', new RequiredValidator()], + ['validator', 'email', new EmailValidator()], + ]); + + $this->validator = new Validator($this->registry, $this->resultProcessor); + } + + public function testValidateWithValidObject(): void + { + $testObject = new class { + #[Validate(processors: ['required', 'email'])] + public string $email = 'walmir.silva@example.com'; + }; + + $expectedResult = new ValidationResult(); + $expectedResult->setValidatedData('email', 'walmir.silva@example.com'); + + $this->resultProcessor + ->expects($this->once()) + ->method('process') + ->willReturn($expectedResult); + + $result = $this->validator->validate($testObject); + + $this->assertFalse($result->hasErrors()); + $this->assertEquals(['email' => 'walmir.silva@example.com'], $result->getValidatedData()); + } + + public function testValidateWithInvalidObject(): void + { + $testObject = new class { + #[Validate(processors: ['required', 'email'])] + public string $email = 'invalid-email'; + }; + + $resultWithErrors = new ValidationResult(); + $resultWithErrors->addError('email', 'invalidFormat', 'Invalid email format'); + + $this->resultProcessor + ->expects($this->once()) + ->method('process') + ->willReturn($resultWithErrors); + + $result = $this->validator->validate($testObject); + + $this->assertTrue($result->hasErrors()); + $this->assertArrayHasKey('email', $result->getErrors()); + } + + public function testValidateWithNoAttributes(): void + { + $testObject = new class { + public string $name = 'Test'; + }; + + $emptyResult = new ValidationResult(); + + $this->resultProcessor + ->expects($this->once()) + ->method('process') + ->willReturn($emptyResult); + + $result = $this->validator->validate($testObject); + + $this->assertFalse($result->hasErrors()); + $this->assertEmpty($result->getValidatedData()); + } + + public function testValidateWithNullObject(): void + { + $testObject = new \stdClass(); + + $emptyResult = new ValidationResult(); + + $this->resultProcessor + ->expects($this->once()) + ->method('process') + ->willReturn($emptyResult); + + $result = $this->validator->validate($testObject); + + $this->assertFalse($result->hasErrors()); + $this->assertEmpty($result->getValidatedData()); + } + + public function testValidateWithMultipleProperties(): void + { + $testObject = new class { + #[Validate(processors: ['required'])] + public string $name = 'Walmir'; + + #[Validate(processors: ['required'])] + public string $email = 'walmir.silva@example.com'; + + public string $unvalidated = 'Skip this'; + }; + + $multiPropertyResult = new ValidationResult(); + $multiPropertyResult->setValidatedData('name', 'Walmir'); + $multiPropertyResult->setValidatedData('email', 'walmir.silva@example.com'); + + $this->resultProcessor + ->expects($this->once()) + ->method('process') + ->willReturn($multiPropertyResult); + + $result = $this->validator->validate($testObject); + + $this->assertFalse($result->hasErrors()); + $this->assertEquals([ + 'name' => 'Walmir', + 'email' => 'walmir.silva@example.com', + ], $result->getValidatedData()); + $this->assertArrayNotHasKey('unvalidated', $result->getValidatedData()); + } + + public function testConstructorWithDefaultResultProcessor(): void + { + $validator = new Validator($this->registry); + + $testObject = new class { + #[Validate(processors: ['required'])] + public string $name = 'Test'; + }; + + $result = $validator->validate($testObject); + + $this->assertInstanceOf(ValidationResult::class, $result); + } + + public function testValidateWithNonObject(): void + { + $this->expectException(\TypeError::class); + $this->validator->validate('not an object'); + } +} From 3fceecd54c86ebc2fdd7db7a82dc2fc0ea2e3042 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 18:20:15 -0300 Subject: [PATCH 10/12] test: add unit test for DateValidatorException - Created a new unit test for DateValidatorException to ensure proper validation error handling. - new file: tests/Exception/DateValidatorExceptionTest.php --- .../Exception/DateValidatorExceptionTest.php | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 tests/Exception/DateValidatorExceptionTest.php diff --git a/tests/Exception/DateValidatorExceptionTest.php b/tests/Exception/DateValidatorExceptionTest.php new file mode 100644 index 0000000..4ef8ee4 --- /dev/null +++ b/tests/Exception/DateValidatorExceptionTest.php @@ -0,0 +1,194 @@ +assertInstanceOf(AbstractException::class, $exception); + $this->assertInstanceOf(DateValidatorException::class, $exception); + } + + public function testGetErrorCode(): void + { + $exception = DateValidatorException::invalidDateFormat('Y-m-d', '31/12/2023'); + + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + public function testGetCode(): void + { + $exception = DateValidatorException::invalidDateFormat('Y-m-d', '31/12/2023'); + + $this->assertEquals(4002, $exception->getCode()); + } + + public function testInvalidDateFormatException(): void + { + $expectedFormat = 'Y-m-d'; + $providedDate = '31/12/2023'; + + $exception = DateValidatorException::invalidDateFormat($expectedFormat, $providedDate); + + $this->assertInstanceOf(DateValidatorException::class, $exception); + $this->assertEquals( + "Invalid date format. Expected: 'Y-m-d', but got: '31/12/2023'.", + $exception->getMessage() + ); + $this->assertEquals(4002, $exception->getCode()); + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + /** + * @dataProvider provideDifferentDateFormats + */ + public function testInvalidDateFormatWithDifferentFormats( + string $format, + string $date, + string $expectedMessage + ): void { + $exception = DateValidatorException::invalidDateFormat($format, $date); + + $this->assertEquals($expectedMessage, $exception->getMessage()); + $this->assertEquals(4002, $exception->getCode()); + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + /** + * @return array + */ + public static function provideDifferentDateFormats(): array + { + return [ + 'DMY format' => [ + 'format' => 'd/m/Y', + 'date' => '2023-12-31', + 'expectedMessage' => "Invalid date format. Expected: 'd/m/Y', but got: '2023-12-31'.", + ], + 'YMD with dots' => [ + 'format' => 'Y.m.d', + 'date' => '31-12-2023', + 'expectedMessage' => "Invalid date format. Expected: 'Y.m.d', but got: '31-12-2023'.", + ], + 'MDY format' => [ + 'format' => 'm-d-Y', + 'date' => '2023/12/31', + 'expectedMessage' => "Invalid date format. Expected: 'm-d-Y', but got: '2023/12/31'.", + ], + ]; + } + + public function testExceptionWithSpecialCharacters(): void + { + $expectedFormat = 'Y-m-d'; + $providedDate = "2023-12-31'--\""; + + $exception = DateValidatorException::invalidDateFormat($expectedFormat, $providedDate); + + $message = $exception->getMessage(); + + $this->assertStringContainsString("Expected: 'Y-m-d'", $message); + $this->assertStringContainsString("2023-12-31'--\"", $message); + $this->assertStringStartsWith('Invalid date format.', $message); + $this->assertEquals(4002, $exception->getCode()); + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + /** + * @dataProvider provideEmptyValues + */ + public function testExceptionWithEmptyValues( + string $format, + string $date, + string $expectedMessage + ): void { + $exception = DateValidatorException::invalidDateFormat($format, $date); + + $this->assertEquals($expectedMessage, $exception->getMessage()); + $this->assertEquals(4002, $exception->getCode()); + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + /** + * @return array + */ + public static function provideEmptyValues(): array + { + return [ + 'empty format' => [ + 'format' => '', + 'date' => '2023-12-31', + 'expectedMessage' => "Invalid date format. Expected: '', but got: '2023-12-31'.", + ], + 'empty date' => [ + 'format' => 'Y-m-d', + 'date' => '', + 'expectedMessage' => "Invalid date format. Expected: 'Y-m-d', but got: ''.", + ], + 'both empty' => [ + 'format' => '', + 'date' => '', + 'expectedMessage' => "Invalid date format. Expected: '', but got: ''.", + ], + ]; + } + + public function testExceptionConstantsValues(): void + { + $reflection = new \ReflectionClass(DateValidatorException::class); + + $codeConstant = $reflection->getConstant('CODE_INVALID_FORMAT'); + $errorCodeConstant = $reflection->getConstant('ERROR_CODE'); + + $exception = DateValidatorException::invalidDateFormat('Y-m-d', '2023-12-31'); + + $this->assertEquals($codeConstant, $exception->getCode()); + $this->assertEquals($errorCodeConstant, $exception->getErrorCode()); + + $this->assertEquals(4002, $codeConstant); + $this->assertEquals('INVALID_DATE_FORMAT', $errorCodeConstant); + } + + /** + * @dataProvider provideUnicodeCharacters + */ + public function testExceptionWithUnicodeCharacters(string $date): void + { + $expectedFormat = 'Y-m-d'; + + $exception = DateValidatorException::invalidDateFormat($expectedFormat, $date); + + $message = $exception->getMessage(); + + $this->assertStringContainsString("Expected: 'Y-m-d'", $message); + $this->assertStringContainsString($date, $message); + $this->assertStringStartsWith('Invalid date format.', $message); + $this->assertEquals(4002, $exception->getCode()); + $this->assertEquals('INVALID_DATE_FORMAT', $exception->getErrorCode()); + } + + /** + * @return array + */ + public static function provideUnicodeCharacters(): array + { + return [ + 'emojis' => ['date' => '2023-12-31😀😎🎉'], + 'special symbols' => ['date' => '2023-12-31★☺♠'], + 'accents' => ['date' => '2023-12-31éàêç'], + 'mixed characters' => ['date' => '2023-12-31★🎉çé'], + ]; + } +} From 1ce7068950ecd64895308acd0b94de8f4397a885 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 18:29:49 -0300 Subject: [PATCH 11/12] test: adjust unit test for DateRangeValidator exception handling - Modified the test to improve exception handling verification in DateRangeValidator. - modified: tests/Processor/Date/DateRangeValidatorTest.php --- tests/Processor/Date/DateRangeValidatorTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Processor/Date/DateRangeValidatorTest.php b/tests/Processor/Date/DateRangeValidatorTest.php index 94b05c6..e811544 100644 --- a/tests/Processor/Date/DateRangeValidatorTest.php +++ b/tests/Processor/Date/DateRangeValidatorTest.php @@ -4,6 +4,7 @@ namespace KaririCode\Validator\Tests\Processor\Date; +use KaririCode\Validator\Exception\DateValidatorException; use KaririCode\Validator\Exception\MissingProcessorConfigException; use KaririCode\Validator\Processor\Date\DateRangeValidator; use PHPUnit\Framework\TestCase; @@ -120,4 +121,20 @@ public function testCustomDateFormat(): void $this->assertTrue($this->validator->isValid()); $this->assertEmpty($this->validator->getErrorKey()); } + + public function testInvalidDateFormatForBothDates(): void + { + $format = 'Y-m-d'; + $minDate = '01/01/2024'; + + $this->expectException(DateValidatorException::class); + $this->expectExceptionMessage( + sprintf("Invalid date format. Expected: '%s', but got: '%s'.", $format, $minDate) + ); + + $this->validator->configure([ + 'minDate' => $minDate, + 'maxDate' => '31/12/2024', + ]); + } } From d54ac5024c209ab623128505fe35479d6153e984 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 23 Oct 2024 19:06:58 -0300 Subject: [PATCH 12/12] docs(readme): add documentation in English and Brazilian Portuguese - English version with complete validator component documentation - Brazilian Portuguese translation with localized examples - Both versions include: - Detailed feature descriptions - Installation and usage guides - Available validators catalog - Configuration instructions - Development setup - Testing procedures - Integration guides --- README.md | 377 ++++++++++++++++- README.pt-br.md | 381 +++++++++++++++++- src/Processor/Logic/ConditionalValidator.php | 39 -- .../Logic/ConditionalValidatorTest.php | 73 ---- tests/application.php | 2 - 5 files changed, 734 insertions(+), 138 deletions(-) delete mode 100644 src/Processor/Logic/ConditionalValidator.php delete mode 100644 tests/Processor/Logic/ConditionalValidatorTest.php diff --git a/README.md b/README.md index 7d6bb36..57d0627 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,367 @@ -# KaririCode Framework: Sanitizer Component +# KaririCode Framework: Validator Component -[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](README.pt-br.md) +A powerful and flexible data validation component for PHP, part of the KaririCode Framework. It uses attribute-based validation with configurable processors to ensure data integrity and validation in your applications. -![PHP](https://img.shields.io/badge/PHP-777BB4?style=for-the-badge&logo=php&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white) ![PHPUnit](https://img.shields.io/badge/PHPUnit-3776AB?style=for-the-badge&logo=php&logoColor=white) +## Table of Contents + +- [Features](#features) +- [Installation](#installation) +- [Usage](#usage) + - [Basic Usage](#basic-usage) + - [Advanced Usage: User Registration](#advanced-usage-user-registration) +- [Available Validators](#available-validators) + - [Input Validators](#input-validators) + - [Numeric Validators](#numeric-validators) + - [Logic Validators](#logic-validators) + - [Date Validators](#date-validators) +- [Configuration](#configuration) +- [Integration with Other KaririCode Components](#integration-with-other-kariricode-components) +- [Development and Testing](#development-and-testing) +- [Contributing](#contributing) +- [License](#license) +- [Support and Community](#support-and-community) + +## Features + +- Attribute-based validation for object properties +- Comprehensive set of built-in validators for common use cases +- Easy integration with other KaririCode components +- Configurable processors for customized validation logic +- Support for custom error messages +- Extensible architecture allowing custom validators +- Robust error handling and reporting +- Chainable validation pipelines for complex data validation +- Built-in support for multiple validation scenarios +- Type-safe validation with PHP 8.3 features + +## Installation + +You can install the Validator component via Composer: + +```bash +composer require kariricode/validator +``` + +### Requirements + +- PHP 8.3 or higher +- Composer +- Extensions: `ext-mbstring`, `ext-filter` + +## Usage + +### Basic Usage + +1. Define your data class with validation attributes: + +```php +use KaririCode\Validator\Attribute\Validate; + +class UserProfile +{ + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + messages: [ + 'required' => 'Username is required', + 'length' => 'Username must be between 3 and 20 characters', + ] + )] + private string $username = ''; + + #[Validate( + processors: ['required', 'email'], + messages: [ + 'required' => 'Email is required', + 'email' => 'Invalid email format', + ] + )] + private string $email = ''; + + // Getters and setters... +} +``` + +2. Set up the validator and use it: + +```php +use KaririCode\ProcessorPipeline\ProcessorRegistry; +use KaririCode\Validator\Validator; +use KaririCode\Validator\Processor\Logic\RequiredValidator; +use KaririCode\Validator\Processor\Input\LengthValidator; +use KaririCode\Validator\Processor\Input\EmailValidator; + +$registry = new ProcessorRegistry(); +$registry->register('validator', 'required', new RequiredValidator()); +$registry->register('validator', 'length', new LengthValidator()); +$registry->register('validator', 'email', new EmailValidator()); + +$validator = new Validator($registry); + +$userProfile = new UserProfile(); +$userProfile->setUsername('wa'); // Too short +$userProfile->setEmail('invalid-email'); // Invalid format + +$result = $validator->validate($userProfile); + +if ($result->hasErrors()) { + foreach ($result->getErrors() as $property => $errors) { + foreach ($errors as $error) { + echo "$property: {$error['message']}\n"; + } + } +} +``` + +### Advanced Usage: User Registration + +Here's an example of how to use the KaririCode Validator in a real-world scenario, such as validating user registration data: + +```php +use KaririCode\Validator\Attribute\Validate; + +class UserRegistration +{ + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + messages: [ + 'required' => 'Username is required', + 'length' => 'Username must be between 3 and 20 characters', + ] + )] + private string $username = ''; + + #[Validate( + processors: ['required', 'email'], + messages: [ + 'required' => 'Email is required', + 'email' => 'Invalid email format', + ] + )] + private string $email = ''; + + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 8], + ], + messages: [ + 'required' => 'Password is required', + 'length' => 'Password must be at least 8 characters long', + ] + )] + private string $password = ''; + + #[Validate( + processors: [ + 'required', + 'integer', + 'range' => ['min' => 18, 'max' => 120], + ], + messages: [ + 'required' => 'Age is required', + 'integer' => 'Age must be a whole number', + 'range' => 'Age must be between 18 and 120', + ] + )] + private int $age = 0; + + // Getters and setters... +} + +// Usage example +$registration = new UserRegistration(); +$registration->setUsername('wm'); // Too short +$registration->setEmail('invalid'); // Invalid format +$registration->setPassword('weak'); // Too short +$registration->setAge(15); // Too young + +$result = $validator->validate($registration); + +// Process validation results +if ($result->hasErrors()) { + $errors = $result->getErrors(); + // Handle validation errors +} else { + $validatedData = $result->getValidatedData(); + // Process valid registration +} +``` + +## Available Validators + +### Input Validators + +- **EmailValidator**: Validates email addresses using PHP's filter_var function. + + - Error Keys: + - `invalidType`: Input is not a string + - `invalidFormat`: Invalid email format + +- **LengthValidator**: Validates string length within specified bounds. + + - **Configuration Options**: + - `minLength`: Minimum allowed length + - `maxLength`: Maximum allowed length + - Error Keys: + - `invalidType`: Input is not a string + - `tooShort`: String is shorter than minLength + - `tooLong`: String is longer than maxLength + +- **UrlValidator**: Validates URLs using PHP's filter_var function. + - Error Keys: + - `invalidType`: Input is not a string + - `invalidFormat`: Invalid URL format + +### Numeric Validators + +- **IntegerValidator**: Ensures the input is a valid integer. + + - Error Keys: + - `notAnInteger`: Input is not a valid integer + +- **RangeValidator**: Validates numeric values within a specified range. + - **Configuration Options**: + - `min`: Minimum allowed value + - `max`: Maximum allowed value + - Error Keys: + - `notNumeric`: Input is not a number + - `outOfRange`: Value is outside specified range + +### Logic Validators + +- **RequiredValidator**: Ensures a value is not empty. + - Error Keys: + - `missingValue`: Required value is missing or empty + +### Date Validators + +- **DateFormatValidator**: Validates dates against a specified format. + + - **Configuration Options**: + - `format`: Date format string (default: 'Y-m-d') + - Error Keys: + - `invalidType`: Input is not a string + - `invalidFormat`: Date doesn't match specified format + +- **DateRangeValidator**: Validates dates within a specified range. + - **Configuration Options**: + - `minDate`: Minimum allowed date + - `maxDate`: Maximum allowed date + - `format`: Date format string (default: 'Y-m-d') + - Error Keys: + - `invalidType`: Input is not a string + - `invalidDate`: Invalid date format + - `outOfRange`: Date is outside specified range + +## Configuration + +The Validator component can be configured globally or per-validator basis. Here's an example of how to configure the `LengthValidator`: + +```php +use KaririCode\Validator\Processor\Input\LengthValidator; + +$lengthValidator = new LengthValidator(); +$lengthValidator->configure([ + 'minLength' => 3, + 'maxLength' => 20, +]); + +$registry->register('validator', 'length', $lengthValidator); +``` + +## Integration with Other KaririCode Components + +The Validator component is designed to work seamlessly with other KaririCode components: + +- **KaririCode\Contract**: Provides interfaces and contracts for consistent component integration. +- **KaririCode\ProcessorPipeline**: Utilized for building and executing validation pipelines. +- **KaririCode\PropertyInspector**: Used for analyzing and processing object properties with validation attributes. + +## Registry Explanation + +The registry is a central component for managing validators. Here's how to set up a complete registry: + +```php +// Create and configure the registry +$registry = new ProcessorRegistry(); + +// Register all required validators +$registry->register('validator', 'required', new RequiredValidator()); +$registry->register('validator', 'email', new EmailValidator()); +$registry->register('validator', 'length', new LengthValidator()); +$registry->register('validator', 'integer', new IntegerValidator()); +$registry->register('validator', 'range', new RangeValidator()); +$registry->register('validator', 'url', new UrlValidator()); +$registry->register('validator', 'dateFormat', new DateFormatValidator()); +$registry->register('validator', 'dateRange', new DateRangeValidator()); +``` + +## Development and Testing + +For development and testing purposes, this package uses Docker and Docker Compose to ensure consistency across different environments. A Makefile is provided for convenience. + +### Prerequisites + +- Docker +- Docker Compose +- Make (optional, but recommended for easier command execution) + +### Development Setup + +1. Clone the repository: + + ```bash + git clone https://github.com/KaririCode-Framework/kariricode-validator.git + cd kariricode-validator + ``` + +2. Set up the environment: + + ```bash + make setup-env + ``` + +3. Start the Docker containers: + + ```bash + make up + ``` + +4. Install dependencies: + + ```bash + make composer-install + ``` + +### Available Make Commands + +- `make up`: Start all services in the background +- `make down`: Stop and remove all containers +- `make build`: Build Docker images +- `make shell`: Access the PHP container shell +- `make test`: Run tests +- `make coverage`: Run test coverage with visual formatting +- `make cs-fix`: Run PHP CS Fixer to fix code style +- `make quality`: Run all quality commands (cs-check, test, security-check) + +## Contributing + +We welcome contributions to the KaririCode Validator component! Here's how you can contribute: + +1. Fork the repository +2. Create a new branch for your feature or bug fix +3. Write tests for your changes +4. Implement your changes +5. Run the test suite and ensure all tests pass +6. Submit a pull request with a clear description of your changes + +Please read our [Contributing Guide](CONTRIBUTING.md) for more details on our code of conduct and development process. ## License @@ -10,15 +369,11 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## Support and Community -- **Documentation**: [https://kariricode.org/docs/dotenv](https://kariricode.org/docs/dotenv) +- **Documentation**: [https://kariricode.org/docs/validator](https://kariricode.org/docs/validator) - **Issue Tracker**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-validator/issues) -- **Community**: [KaririCode Club Community](https://kariricode.club) - -## Acknowledgments - -- The KaririCode Framework team and contributors. -- Inspired by other popular PHP Dotenv libraries. +- **Community Forum**: [KaririCode Club Community](https://kariricode.club) +- **Stack Overflow**: Tag your questions with `kariricode-validator` --- -Built with ❤️ by the KaririCode team. Empowering developers to build more robust and flexible PHP applications. +Built with ❤️ by the KaririCode team. Empowering developers to create more secure and robust PHP applications. diff --git a/README.pt-br.md b/README.pt-br.md index 46777c0..f780333 100644 --- a/README.pt-br.md +++ b/README.pt-br.md @@ -1,24 +1,379 @@ -# KaririCode Framework: Componente Dotenv +# Framework KaririCode: Componente Validator -[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](README.pt-br.md) +Um componente de validação de dados poderoso e flexível para PHP, parte do Framework KaririCode. Utiliza validação baseada em atributos com processadores configuráveis para garantir a integridade e validação dos dados em suas aplicações. -![PHP](https://img.shields.io/badge/PHP-777BB4?style=for-the-badge&logo=php&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white) ![PHPUnit](https://img.shields.io/badge/PHPUnit-3776AB?style=for-the-badge&logo=php&logoColor=white) +## Índice -## Licença +- [Características](#características) +- [Instalação](#instalação) +- [Uso](#uso) + - [Uso Básico](#uso-básico) + - [Uso Avançado: Registro de Usuário](#uso-avançado-registro-de-usuário) +- [Validadores Disponíveis](#validadores-disponíveis) + - [Validadores de Entrada](#validadores-de-entrada) + - [Validadores Numéricos](#validadores-numéricos) + - [Validadores Lógicos](#validadores-lógicos) + - [Validadores de Data](#validadores-de-data) +- [Configuração](#configuração) +- [Integração com Outros Componentes KaririCode](#integração-com-outros-componentes-kariricode) +- [Desenvolvimento e Testes](#desenvolvimento-e-testes) +- [Contribuindo](#contribuindo) +- [Licença](#licença) +- [Suporte e Comunidade](#suporte-e-comunidade) -Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para mais detalhes. +## Características -## Suporte e Comunidade +- Validação baseada em atributos para propriedades de objetos +- Conjunto abrangente de validadores integrados para casos de uso comuns +- Fácil integração com outros componentes KaririCode +- Processadores configuráveis para lógica de validação personalizada +- Suporte para mensagens de erro personalizadas +- Arquitetura extensível permitindo validadores personalizados +- Tratamento e relatório de erros robusto +- Pipelines de validação encadeáveis para validação complexa de dados +- Suporte integrado para múltiplos cenários de validação +- Validação segura de tipos com recursos do PHP 8.3 + +## Instalação + +Você pode instalar o componente Validator via Composer: + +```bash +composer require kariricode/validator +``` + +### Requisitos + +- PHP 8.3 ou superior +- Composer +- Extensões: `ext-mbstring`, `ext-filter` + +## Uso + +### Uso Básico + +1. Defina sua classe de dados com atributos de validação: + +```php +use KaririCode\Validator\Attribute\Validate; + +class PerfilUsuario +{ + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + messages: [ + 'required' => 'Nome de usuário é obrigatório', + 'length' => 'Nome de usuário deve ter entre 3 e 20 caracteres', + ] + )] + private string $username = ''; + + #[Validate( + processors: ['required', 'email'], + messages: [ + 'required' => 'Email é obrigatório', + 'email' => 'Formato de email inválido', + ] + )] + private string $email = ''; + + // Getters e setters... +} +``` + +2. Configure o validador e use-o: + +```php +use KaririCode\ProcessorPipeline\ProcessorRegistry; +use KaririCode\Validator\Validator; +use KaririCode\Validator\Processor\Logic\RequiredValidator; +use KaririCode\Validator\Processor\Input\LengthValidator; +use KaririCode\Validator\Processor\Input\EmailValidator; + +$registry = new ProcessorRegistry(); +$registry->register('validator', 'required', new RequiredValidator()); +$registry->register('validator', 'length', new LengthValidator()); +$registry->register('validator', 'email', new EmailValidator()); + +$validator = new Validator($registry); + +$perfilUsuario = new PerfilUsuario(); +$perfilUsuario->setUsername('wa'); // Muito curto +$perfilUsuario->setEmail('email-invalido'); // Formato inválido + +$resultado = $validator->validate($perfilUsuario); + +if ($resultado->hasErrors()) { + foreach ($resultado->getErrors() as $propriedade => $erros) { + foreach ($erros as $erro) { + echo "$propriedade: {$erro['message']}\n"; + } + } +} +``` + +### Uso Avançado: Registro de Usuário + +Aqui está um exemplo de como usar o Validator KaririCode em um cenário do mundo real, como validação de dados de registro de usuário: + +```php +use KaririCode\Validator\Attribute\Validate; + +class RegistroUsuario +{ + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 3, 'maxLength' => 20], + ], + messages: [ + 'required' => 'Nome de usuário é obrigatório', + 'length' => 'Nome de usuário deve ter entre 3 e 20 caracteres', + ] + )] + private string $username = ''; + + #[Validate( + processors: ['required', 'email'], + messages: [ + 'required' => 'Email é obrigatório', + 'email' => 'Formato de email inválido', + ] + )] + private string $email = ''; + + #[Validate( + processors: [ + 'required', + 'length' => ['minLength' => 8], + ], + messages: [ + 'required' => 'Senha é obrigatória', + 'length' => 'Senha deve ter pelo menos 8 caracteres', + ] + )] + private string $password = ''; + + #[Validate( + processors: [ + 'required', + 'integer', + 'range' => ['min' => 18, 'max' => 120], + ], + messages: [ + 'required' => 'Idade é obrigatória', + 'integer' => 'Idade deve ser um número inteiro', + 'range' => 'Idade deve estar entre 18 e 120', + ] + )] + private int $age = 0; + + // Getters e setters... +} + +// Exemplo de uso +$registro = new RegistroUsuario(); +$registro->setUsername('wm'); // Muito curto +$registro->setEmail('invalido'); // Formato inválido +$registro->setPassword('fraca'); // Muito curta +$registro->setAge(15); // Muito jovem + +$resultado = $validator->validate($registro); + +// Processa resultados da validação +if ($resultado->hasErrors()) { + $erros = $resultado->getErrors(); + // Trata erros de validação +} else { + $dadosValidados = $resultado->getValidatedData(); + // Processa registro válido +} +``` + +## Validadores Disponíveis + +### Validadores de Entrada + +- **EmailValidator**: Valida endereços de email usando a função filter_var do PHP. + + - Chaves de Erro: + - `invalidType`: Entrada não é uma string + - `invalidFormat`: Formato de email inválido + +- **LengthValidator**: Valida comprimento da string dentro dos limites especificados. + + - **Opções de Configuração**: + - `minLength`: Comprimento mínimo permitido + - `maxLength`: Comprimento máximo permitido + - Chaves de Erro: + - `invalidType`: Entrada não é uma string + - `tooShort`: String é menor que minLength + - `tooLong`: String é maior que maxLength + +- **UrlValidator**: Valida URLs usando a função filter_var do PHP. + - Chaves de Erro: + - `invalidType`: Entrada não é uma string + - `invalidFormat`: Formato de URL inválido + +### Validadores Numéricos + +- **IntegerValidator**: Garante que a entrada seja um inteiro válido. + + - Chaves de Erro: + - `notAnInteger`: Entrada não é um inteiro válido -- **Documentação**: [https://kariricode.org/docs/dotenv](https://kariricode.org/docs/dotenv) -- **Rastreador de Problemas**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-validator/issues) -- **Comunidade**: [Comunidade KaririCode Club](https://kariricode.club) +- **RangeValidator**: Valida valores numéricos dentro de um intervalo especificado. + - **Opções de Configuração**: + - `min`: Valor mínimo permitido + - `max`: Valor máximo permitido + - Chaves de Erro: + - `notNumeric`: Entrada não é um número + - `outOfRange`: Valor está fora do intervalo especificado -## Agradecimentos +### Validadores Lógicos + +- **RequiredValidator**: Garante que um valor não esteja vazio. + - Chaves de Erro: + - `missingValue`: Valor obrigatório está faltando ou vazio + +### Validadores de Data + +- **DateFormatValidator**: Valida datas contra um formato especificado. + + - **Opções de Configuração**: + - `format`: String de formato de data (padrão: 'Y-m-d') + - Chaves de Erro: + - `invalidType`: Entrada não é uma string + - `invalidFormat`: Data não corresponde ao formato especificado + +- **DateRangeValidator**: Valida datas dentro de um intervalo especificado. + - **Opções de Configuração**: + - `minDate`: Data mínima permitida + - `maxDate`: Data máxima permitida + - `format`: String de formato de data (padrão: 'Y-m-d') + - Chaves de Erro: + - `invalidType`: Entrada não é uma string + - `invalidDate`: Formato de data inválido + - `outOfRange`: Data está fora do intervalo especificado + +## Configuração + +O componente Validator pode ser configurado globalmente ou por validador. Aqui está um exemplo de como configurar o `LengthValidator`: + +```php +use KaririCode\Validator\Processor\Input\LengthValidator; + +$lengthValidator = new LengthValidator(); +$lengthValidator->configure([ + 'minLength' => 3, + 'maxLength' => 20, +]); + +$registry->register('validator', 'length', $lengthValidator); +``` + +## Integração com Outros Componentes KaririCode + +O componente Validator foi projetado para trabalhar perfeitamente com outros componentes KaririCode: + +- **KaririCode\Contract**: Fornece interfaces e contratos para integração consistente de componentes. +- **KaririCode\ProcessorPipeline**: Utilizado para construir e executar pipelines de validação. +- **KaririCode\PropertyInspector**: Usado para analisar e processar propriedades de objetos com atributos de validação. + +## Explicação do Registry + +O registry é um componente central para gerenciar validadores. Aqui está como configurar um registry completo: + +```php +// Cria e configura o registry +$registry = new ProcessorRegistry(); + +// Registra todos os validadores necessários +$registry->register('validator', 'required', new RequiredValidator()); +$registry->register('validator', 'email', new EmailValidator()); +$registry->register('validator', 'length', new LengthValidator()); +$registry->register('validator', 'integer', new IntegerValidator()); +$registry->register('validator', 'range', new RangeValidator()); +$registry->register('validator', 'url', new UrlValidator()); +$registry->register('validator', 'dateFormat', new DateFormatValidator()); +$registry->register('validator', 'dateRange', new DateRangeValidator()); +``` + +## Desenvolvimento e Testes + +Para fins de desenvolvimento e teste, este pacote usa Docker e Docker Compose para garantir consistência em diferentes ambientes. Um Makefile é fornecido para conveniência. + +### Pré-requisitos + +- Docker +- Docker Compose +- Make (opcional, mas recomendado para execução mais fácil de comandos) + +### Configuração de Desenvolvimento + +1. Clone o repositório: + + ```bash + git clone https://github.com/KaririCode-Framework/kariricode-validator.git + cd kariricode-validator + ``` + +2. Configure o ambiente: + + ```bash + make setup-env + ``` + +3. Inicie os containers Docker: + + ```bash + make up + ``` + +4. Instale as dependências: + + ```bash + make composer-install + ``` + +### Comandos Make Disponíveis + +- `make up`: Inicia todos os serviços em segundo plano +- `make down`: Para e remove todos os containers +- `make build`: Constrói imagens Docker +- `make shell`: Acessa o shell do container PHP +- `make test`: Executa testes +- `make coverage`: Executa cobertura de testes com formatação visual +- `make cs-fix`: Executa PHP CS Fixer para corrigir estilo de código +- `make quality`: Executa todos os comandos de qualidade (cs-check, test, security-check) + +## Contribuindo + +Nós recebemos contribuições para o componente KaririCode Validator! Aqui está como você pode contribuir: + +1. Faça um fork do repositório +2. Crie um novo branch para sua feature ou correção de bug +3. Escreva testes para suas alterações +4. Implemente suas alterações +5. Execute a suite de testes e garanta que todos os testes passem +6. Envie um pull request com uma descrição clara de suas alterações + +Por favor, leia nosso [Guia de Contribuição](CONTRIBUTING.md) para mais detalhes sobre nosso código de conduta e processo de desenvolvimento. + +## Licença + +Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes. + +## Suporte e Comunidade -- A equipe do KaririCode Framework e contribuidores. -- Inspirado por outras bibliotecas populares de Dotenv para PHP. +- **Documentação**: [https://kariricode.org/docs/validator](https://kariricode.org/docs/validator) +- **Issue Tracker**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-validator/issues) +- **Fórum da Comunidade**: [Comunidade KaririCode Club](https://kariricode.club) +- **Stack Overflow**: Marque suas perguntas com `kariricode-validator` --- -Construído com ❤️ pela equipe KaririCode. Capacitando desenvolvedores a criar aplicações PHP mais robustas e flexíveis. +Construído com ❤️ pela equipe KaririCode. Capacitando desenvolvedores para criar aplicações PHP mais seguras e robustas. diff --git a/src/Processor/Logic/ConditionalValidator.php b/src/Processor/Logic/ConditionalValidator.php deleted file mode 100644 index 1c34122..0000000 --- a/src/Processor/Logic/ConditionalValidator.php +++ /dev/null @@ -1,39 +0,0 @@ -condition = $options['condition']; - - if (!isset($options['validator']) || !($options['validator'] instanceof \Closure)) { - throw new \InvalidArgumentException('Validator must be a Closure'); - } - $this->validator = $options['validator']; - } - - public function process(mixed $input): mixed - { - if (($this->condition)($input)) { - $validationResult = ($this->validator)($input); - if (true !== $validationResult) { - $this->setInvalid('conditionNotMet'); - } - } - - return $input; - } -} diff --git a/tests/Processor/Logic/ConditionalValidatorTest.php b/tests/Processor/Logic/ConditionalValidatorTest.php deleted file mode 100644 index 92e8ee2..0000000 --- a/tests/Processor/Logic/ConditionalValidatorTest.php +++ /dev/null @@ -1,73 +0,0 @@ -validator = new ConditionalValidator(); - } - - public function testValidCondition(): void - { - $this->validator->configure([ - 'condition' => fn ($input) => $input > 0, - 'validator' => fn ($input) => $input < 10, - ]); - - $this->validator->process(5); - - $this->assertTrue($this->validator->isValid()); - $this->assertEmpty($this->validator->getErrorKey()); - } - - public function testInvalidCondition(): void - { - $this->validator->configure([ - 'condition' => fn ($input) => $input > 0, - 'validator' => fn ($input) => $input < 10, - ]); - - $this->validator->process(15); - - $this->assertFalse($this->validator->isValid()); - $this->assertSame('conditionNotMet', $this->validator->getErrorKey()); - } - - public function testConditionNotMet(): void - { - $this->validator->configure([ - 'condition' => fn ($input) => $input > 100, - 'validator' => fn ($input) => $input < 10, - ]); - - $this->validator->process(50); - - $this->assertTrue($this->validator->isValid()); - $this->assertEmpty($this->validator->getErrorKey()); - } - - public function testMissingConditionThrowsException(): void - { - $this->expectException(\InvalidArgumentException::class); - $this->validator->configure([ - 'validator' => fn ($input) => true, - ]); - } - - public function testMissingValidatorThrowsException(): void - { - $this->expectException(\InvalidArgumentException::class); - $this->validator->configure([ - 'condition' => fn ($input) => true, - ]); - } -} diff --git a/tests/application.php b/tests/application.php index 90472d1..605351a 100644 --- a/tests/application.php +++ b/tests/application.php @@ -11,7 +11,6 @@ use KaririCode\Validator\Processor\Input\EmailValidator; use KaririCode\Validator\Processor\Input\LengthValidator; use KaririCode\Validator\Processor\Input\UrlValidator; -use KaririCode\Validator\Processor\Logic\ConditionalValidator; use KaririCode\Validator\Processor\Logic\RequiredValidator; use KaririCode\Validator\Processor\Numeric\IntegerValidator; use KaririCode\Validator\Processor\Numeric\RangeValidator; @@ -153,7 +152,6 @@ public function setBirthDate(string $birthDate): void $registry->register('validator', 'range', new RangeValidator()); $registry->register('validator', 'url', new UrlValidator()); $registry->register('validator', 'dateFormat', new DateFormatValidator()); -$registry->register('validator', 'conditional', new ConditionalValidator()); $validator = new Validator($registry);