diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9d93559..aba6b58 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,115 +1,113 @@ -name: 'CI' +name: CI on: - push: - branches: - - master - pull_request: + workflow_dispatch: ~ + push: + branches: + - master + pull_request: ~ jobs: - lint: - name: 'Lint' - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - - - name: 'Setup PHP' - uses: shivammathur/setup-php@v2 - with: - coverage: "none" - extensions: "json" - ini-values: "memory_limit=-1" - php-version: "8.0" - - - name: 'Determine composer cache directory' - id: composer-cache - run: echo "::set-output name=directory::$(composer config cache-dir)" - - - name: 'Cache composer dependencies' - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.directory }} - key: 7.4-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: 7.4-composer- - - - name: 'Install dependencies' - id: deps - run: | - echo "::group::composer update" - composer update --no-progress --ansi - echo "::endgroup::" - - echo "::group::install phpunit" - # Required for PhpStan - vendor/bin/simple-phpunit install - echo "::endgroup::" - - - name: 'Composer validate' - if: always() && steps.deps.outcome == 'success' - run: composer validate --strict - - - name: 'PHP CS Fixer' - if: always() && steps.deps.outcome == 'success' - run: vendor/bin/php-cs-fixer fix --dry-run --diff - - - name: 'PhpStan' - if: always() && steps.deps.outcome == 'success' - run: vendor/bin/phpstan analyse - - tests: - name: 'Tests' - runs-on: ubuntu-latest - timeout-minutes: 5 - - strategy: - fail-fast: false # don't cancel other matrix jobs on failure - matrix: - php: [ '7.4', '8.0' ] - - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - - - name: 'Setup PHP' - uses: shivammathur/setup-php@v2 - with: - coverage: "none" - extensions: "json" - ini-values: "memory_limit=-1" - php-version: "${{ matrix.php }}" - - - name: 'Determine composer cache directory' - id: composer-cache - run: echo "::set-output name=directory::$(composer config cache-dir)" - - - name: 'Cache composer dependencies' - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.directory }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- - - #- name: 'Fixup Composer' - # if: matrix.php == 8.0 - # run: | - # echo "::group::Fixup Composer platform config for third-parties deps not PHP 8 ready yet" - # composer config platform.php 7.4.99 - # echo "::endgroup::" - - - name: 'Install dependencies' - run: | - echo "::group::composer update" - composer update --no-progress --ansi - echo "::endgroup::" - - echo "::group::install phpunit" - vendor/bin/simple-phpunit install - echo "::endgroup::" - - - name: 'Run tests' - run: vendor/bin/simple-phpunit --testdox - + lint: + name: Lint + runs-on: 'ubuntu-latest' + timeout-minutes: 5 + + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + + - name: 'Setup PHP' + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + + - name: 'Install dependencies' + run: | + make install + vendor/bin/simple-phpunit --version + + - name: 'Check style' + run: make lint + + test: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + timeout-minutes: 8 + continue-on-error: ${{ matrix.allow-failure == 1 }} + + strategy: + fail-fast: false + matrix: + include: + # Lowest deps + - name: 'Test lowest deps Symfony 5.4 [Linux, PHP 7.4]' + os: 'ubuntu-latest' + php: '7.4' + symfony: '5.4.*@dev' + composer-flags: '--prefer-lowest' + allow-unstable: true + + # Most recent versions + - name: 'Test Symfony 5.4 [Linux, PHP 8.0]' + os: 'ubuntu-latest' + php: '8.0' + symfony: '5.4.*@dev' + allow-unstable: true + + - name: 'Test Symfony 6.0 [Linux, PHP 8.1]' + os: 'ubuntu-latest' + php: '8.1' + symfony: '6.0.*@dev' + allow-unstable: true + + - name: 'Test next Symfony 6.4 [Linux, PHP 8.2]' + os: 'ubuntu-latest' + php: '8.2' + symfony: '6.4.*@dev' + allow-unstable: true + + # Bleeding edge (unreleased dev versions where failures are allowed) + - name: 'Test next Symfony [Linux, PHP 8.3] (allowed failure)' + os: 'ubuntu-latest' + php: '8.3' + symfony: '7.0.*@dev' + composer-flags: '--ignore-platform-req php' + allow-unstable: true + allow-failure: true + + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + + - name: 'Setup PHP' + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo_sqlite + coverage: pcov + tools: 'composer:v2,flex' + + - name: 'Get composer cache directory' + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: 'Cache dependencies' + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-${{ hashFiles('**/composer.json') }}-flags-${{ matrix.composer-flags }} + restore-keys: ${{ runner.os }}-composer- + + - name: 'Allow unstable packages' + run: composer config minimum-stability dev + if: ${{ matrix.allow-unstable }} + + - name: 'Install dependencies' + run: composer update --prefer-dist ${{ matrix.composer-flags }} --ansi + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" + + - name: 'Run PHPUnit tests' + run: vendor/bin/simple-phpunit --testdox --verbose ${{ matrix.code-coverage && '--coverage-text --coverage-clover build/logs/clover.xml' }} diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 3a44ec4..4740c52 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,29 +1,38 @@ in([ - __DIR__ -]); +$header = <<<'EOF' +This file is part of the ElaoJsonForm bundle. + +Copyright (C) Elao + +@author Elao +EOF; + +$finder = PhpCsFixer\Finder::create() + ->in([__DIR__]) +; return (new PhpCsFixer\Config()) - ->setFinder($finder) - ->setCacheFile('.php-cs-fixer.cache') // forward compatibility with 3.x line ->setRiskyAllowed(true) + ->setUsingCache(true) + ->setFinder($finder) ->setRules([ - '@PSR12' => true, '@Symfony' => true, - 'strict_param' => true, 'array_syntax' => ['syntax' => 'short'], 'concat_space' => ['spacing' => 'one'], - 'declare_strict_types' => true, + //'header_comment' => ['header' => $header], 'native_function_invocation' => ['include' => ['@compiler_optimized']], - 'no_superfluous_phpdoc_tags' => true, 'ordered_imports' => true, + 'php_unit_namespaced' => true, + 'php_unit_method_casing' => false, 'phpdoc_annotation_without_dot' => false, - 'phpdoc_order' => true, 'phpdoc_summary' => false, - 'simplified_null_return' => false, + 'phpdoc_order' => true, + 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'psr_autoloading' => true, 'single_line_throw' => false, + 'simplified_null_return' => false, 'void_return' => true, - 'yoda_style' => false, + 'yoda_style' => [], ]) ; diff --git a/Form/RequestHandler/JsonHttpFoundationRequestHandler.php b/Form/RequestHandler/JsonHttpFoundationRequestHandler.php index dbb6fb5..d60145e 100644 --- a/Form/RequestHandler/JsonHttpFoundationRequestHandler.php +++ b/Form/RequestHandler/JsonHttpFoundationRequestHandler.php @@ -19,16 +19,11 @@ */ class JsonHttpFoundationRequestHandler extends HttpFoundationRequestHandler { - /** - * Methods that have a body - * - * @var array - */ - private static array $bodyMethods = ['POST', 'PUT', 'PATCH', 'DELETE']; + private const BODY_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE']; private ServerParams $serverParams; - public function __construct(ServerParams $serverParams = null) + public function __construct(?ServerParams $serverParams = null) { parent::__construct($serverParams); @@ -43,10 +38,9 @@ public function handleRequest(FormInterface $form, $request = null): void if (!$request instanceof Request) { throw new UnexpectedTypeException($request, Request::class); } - if ( - 'json' === $request->getContentType() - && \in_array($request->getMethod(), static::$bodyMethods, false) + 'json' === $this->getFormat($request) + && \in_array($request->getMethod(), self::BODY_METHODS, false) ) { $this->handleJsonRequest($form, $request); @@ -56,6 +50,19 @@ public function handleRequest(FormInterface $form, $request = null): void parent::handleRequest($form, $request); } + private function getFormat(Request $request): ?string + { + if (method_exists($request, 'getContentTypeFormat')) { + return $request->getContentTypeFormat(); + } + + if (method_exists($request, 'getContentType')) { + return $request->getContentType(); + } + + throw new \LogicException('Could not get Request format'); + } + /** * Handle Json Request */ @@ -79,7 +86,7 @@ protected function handleJsonRequest(FormInterface $form, Request $request): voi return; } - if ('' === $name || 'DELETE' === $request->getMethod()) { + if ('' === $name || 'DELETE' === $request->getMethod() || !\is_array($content)) { $data = $content; } else { // Don't submit if the form's name does not exist in the request @@ -111,10 +118,12 @@ protected function isContentSizeValid(FormInterface $form): bool if (null !== $maxContentLength && $contentLength > $maxContentLength) { // Submit the form, but don't clear the default values + /** @var string $maxSizeMessage */ + $maxSizeMessage = $form->getConfig()->getOption('post_max_size_message'); $form->submit(null, false); $form->addError( new FormError( - $form->getConfig()->getOption('post_max_size_message'), + $maxSizeMessage, null, ['{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()] ) diff --git a/Makefile b/Makefile index 34185eb..37467c3 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,63 @@ +.SILENT: +.PHONY: test build + +########### +# Helpers # +########### + +## Colors +COLOR_RESET = \033[0m +COLOR_INFO = \033[32m +COLOR_COMMENT = \033[33m + +## Help +help: + printf "${COLOR_COMMENT}Usage:${COLOR_RESET}\n" + printf " make [target]\n\n" + printf "${COLOR_COMMENT}Available targets:${COLOR_RESET}\n" + awk '/^[a-zA-Z\-\_0-9\.@]+:/ { \ + helpMessage = match(lastLine, /^## (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")); \ + helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ + printf " ${COLOR_INFO}%-16s${COLOR_RESET} %s\n", helpCommand, helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + ########### # Install # ########### +## Install application install: - composer update + # Composer + composer install --verbose + +############ +# Security # +############ + +## Run security checks +security: + symfony check:security + +security@test: export APP_ENV = test +security@test: security ######## # Lint # ######## -lint: lint.php-cs-fixer lint.phpstan lint.composer +## Run linters +lint: lint.phpcsfixer lint.phpstan lint.composer + +lint.phpcsfixer: export PHP_CS_FIXER_IGNORE_ENV = true +lint.phpcsfixer: + vendor/bin/php-cs-fixer fix --dry-run --no-interaction --diff -lint.php-cs-fixer: +lint.phpcsfixer-fix: export PHP_CS_FIXER_IGNORE_ENV = true +lint.phpcsfixer-fix: vendor/bin/php-cs-fixer fix lint.phpstan: @@ -24,6 +70,6 @@ lint.composer: # Test # ######## -## Test +## Run tests test: vendor/bin/simple-phpunit diff --git a/Tests/Form/RequestHandler/RequestHandlerTest.php b/Tests/Form/RequestHandler/RequestHandlerTest.php index 69c8f57..b76aa42 100644 --- a/Tests/Form/RequestHandler/RequestHandlerTest.php +++ b/Tests/Form/RequestHandler/RequestHandlerTest.php @@ -57,7 +57,7 @@ public function testClassicPostRequest(): void } /** - * @return array + * @return array> */ private function getSampleData(): array { diff --git a/composer.json b/composer.json index ddbe61b..9afb012 100644 --- a/composer.json +++ b/composer.json @@ -15,16 +15,17 @@ "require": { "php": "^7.4|^8.0", "ext-json": "*", - "symfony/framework-bundle": "~4.4|~5.0|~6.0", - "symfony/form": "~4.4|~5.0|~6.0" + "symfony/framework-bundle": "~5.4|~6.0|~7.0", + "symfony/form": "~5.4|~6.0|~7.0" }, "require-dev": { "symfony/phpunit-bridge": "^5.3", "friendsofphp/php-cs-fixer": "^3.0", - "phpstan/phpstan": "^0.12.94", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-phpunit": "^0.12.21", - "phpstan/phpstan-symfony": "^0.12.41" + "phpstan/phpstan": "^1.10", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-symfony": "^1.3", + "ekino/phpstan-banned-code": "^1.0" }, "autoload": { "psr-4": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index cf79fa9..efd8ee7 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,4 +8,5 @@ parameters: - vendor bootstrapFiles: + - vendor/autoload.php - vendor/bin/.phpunit/phpunit/vendor/autoload.php