From c05ff66ec11f4a3fef276a9e3f18e7982d0f0cde Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Wed, 7 Apr 2021 18:33:47 +0300 Subject: [PATCH 01/12] support php8 --- .php_cs.cache | 1 + .travis.yml | 25 ++++++++++++------------- composer.json | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 .php_cs.cache diff --git a/.php_cs.cache b/.php_cs.cache new file mode 100644 index 0000000..a7f32f5 --- /dev/null +++ b/.php_cs.cache @@ -0,0 +1 @@ +{"php":"7.4.16","version":"2.17.3:v2.17.3#bd32f5dd72cdfc7b53f54077f980e144bfa2f595","indent":" ","lineEnding":"\n","rules":{"array_syntax":{"syntax":"short"},"binary_operator_spaces":{"default":"single_space","operators":{"=>":null}},"blank_line_after_namespace":true,"blank_line_after_opening_tag":true,"blank_line_before_statement":{"statements":["return"]},"braces":true,"cast_spaces":true,"class_attributes_separation":{"elements":["method"]},"class_definition":true,"concat_space":{"spacing":"one"},"declare_equal_normalize":true,"elseif":true,"encoding":true,"full_opening_tag":true,"fully_qualified_strict_types":true,"function_declaration":true,"function_typehint_space":true,"heredoc_to_nowdoc":true,"include":true,"increment_style":{"style":"post"},"indentation_type":true,"linebreak_after_opening_tag":true,"line_ending":true,"lowercase_cast":true,"lowercase_constants":true,"lowercase_keywords":true,"lowercase_static_reference":true,"magic_method_casing":true,"magic_constant_casing":true,"method_argument_space":true,"native_function_casing":true,"no_alias_functions":true,"no_extra_blank_lines":{"tokens":["extra","throw","use","use_trait"]},"no_blank_lines_after_class_opening":true,"no_blank_lines_after_phpdoc":true,"no_closing_tag":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_import_slash":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":{"use":"echo"},"no_multiline_whitespace_around_double_arrow":true,"multiline_whitespace_before_semicolons":{"strategy":"no_multi_line"},"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_after_function_name":true,"no_spaces_around_offset":true,"no_spaces_inside_parenthesis":true,"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"no_unneeded_control_parentheses":true,"no_unreachable_default_argument_value":true,"no_useless_return":true,"no_whitespace_before_comma_in_array":true,"no_whitespace_in_blank_line":true,"normalize_index_brace":true,"not_operator_with_successor_space":true,"object_operator_without_whitespace":true,"ordered_imports":{"sortAlgorithm":"length"},"phpdoc_indent":true,"phpdoc_inline_tag":true,"phpdoc_no_access":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_types":true,"phpdoc_var_without_name":true,"psr4":true,"self_accessor":true,"short_scalar_cast":true,"simplified_null_return":true,"single_blank_line_at_eof":true,"single_blank_line_before_namespace":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"single_line_comment_style":{"comment_types":["hash"]},"single_quote":true,"space_after_semicolon":true,"standardize_not_equals":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"ternary_operator_spaces":true,"trailing_comma_in_multiline_array":true,"trim_array_spaces":true,"unary_operator_spaces":true,"visibility_required":{"elements":["method","property"]},"whitespace_after_comma_in_array":true,"no_unused_imports":true},"hashes":{"config\/config.php":1710567645,"tests\/Support\/TestServiceProvider.php":1054953203,"tests\/Support\/routes.php":2367305398,"tests\/PlaybackTest.php":3239734766,"src\/Recorder.php":3485823478,"src\/RecordedResponse.php":2846671540,"src\/PlaybackServiceProvider.php":2838178533,"src\/Playback.php":3212455048}} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9b57019..587ac0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,24 @@ language: php php: - - 7.3 - - 7.4 + - 7.4 + - 8.0 env: - matrix: - - COMPOSER_FLAGS="--prefer-lowest" - - COMPOSER_FLAGS="" + matrix: + - COMPOSER_FLAGS="--prefer-lowest" + - COMPOSER_FLAGS="" service: - - redis-server + - redis-server before_install: - - mkdir -p ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d - - bash <(curl -s https://raw.githubusercontent.com/php-cache/cache/master/build/php/7.2/Redis.sh) - + - mkdir -p ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d + - bash <(curl -s https://raw.githubusercontent.com/php-cache/cache/master/build/php/7.2/Redis.sh) before_script: - - travis_retry composer self-update - - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source - + - travis_retry composer self-update + - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source + script: - - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover + - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover diff --git a/composer.json b/composer.json index 30ce5e4..944f3ca 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^7.3", + "php": "^7.3|^8.0", "illuminate/support": "^8.0" }, "require-dev": { From e7bbf8e8d83ba5c117b42deaa8f569d2312490ff Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Wed, 7 Apr 2021 18:36:48 +0300 Subject: [PATCH 02/12] fix travis redis ext --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 587ac0b..0ea52ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ service: before_install: - mkdir -p ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d - - bash <(curl -s https://raw.githubusercontent.com/php-cache/cache/master/build/php/7.2/Redis.sh) + - bash <(curl -s https://raw.githubusercontent.com/php-cache/cache/master/build/php/$(phpenv version-name)/Redis.sh) before_script: - travis_retry composer self-update From 956a8f8e1dd5afa16d9a09a8cc00b762164d93a0 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Wed, 7 Apr 2021 18:43:58 +0300 Subject: [PATCH 03/12] update travisci --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ea52ed..d385ea0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ service: before_install: - mkdir -p ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d - - bash <(curl -s https://raw.githubusercontent.com/php-cache/cache/master/build/php/$(phpenv version-name)/Redis.sh) before_script: - travis_retry composer self-update From 55c5397f6d533434dea01a188d36d81f7fdce43f Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:10:29 +0300 Subject: [PATCH 04/12] switch to Github actions --- .github/workflows/test.yml | 65 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 23 -------------- 2 files changed, 65 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e9cd2ed --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,65 @@ +name: run-tests + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + # Disable testing on windows for now... + # os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] + php: [7.4, 8.0, 8.1] + laravel: [7.*, 8.*, 9.*] + #stability: [prefer-lowest, prefer-stable] + stability: [prefer-stable] + include: + - laravel: 7.* + testbench: 5.* + - laravel: 8.* + testbench: ^6.6 + - laravel: 9.* + testbench: 7.* + # Laravel 7 has no support for PHP 8.1 + exclude: + - laravel: 7.* + php: 8.1 + # Older versions of L7 doesn't support PHP 8 + - laravel: 7.* + php: 8 + stability: prefer-lowest + # Laravel 9 doesn't support < PHP 8 + - laravel: 9.* + php: 7.4 + + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + - name: Install dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Execute tests + run: vendor/bin/phpunit \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d385ea0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: php - -php: - - 7.4 - - 8.0 - -env: - matrix: - - COMPOSER_FLAGS="--prefer-lowest" - - COMPOSER_FLAGS="" - -service: - - redis-server - -before_install: - - mkdir -p ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d - -before_script: - - travis_retry composer self-update - - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source - -script: - - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover From fe751a2d4aac7c0f03d6a05d5c3b9cf81cc9e308 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:22:31 +0300 Subject: [PATCH 05/12] update readme, add support for L9 --- README.md | 41 +++++++++++++++++++---------------------- composer.json | 4 ++-- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b1c3bce..4bc609a 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,23 @@ -# Idempotent endpoints in Laravel ร  la Stripe +# Laravel Playback [![Latest Version on Packagist](https://img.shields.io/packagist/v/swiftmade/playback.svg?style=flat-square)](https://packagist.org/packages/swiftmade/playback) -[![Build Status](https://img.shields.io/travis/swiftmade/playback/master.svg?style=flat-square)](https://travis-ci.org/swiftmade/playback) +![GitHub Actions](https://github.com/swiftmade/playback/actions/workflows/test.yml/badge.svg) [![Total Downloads](https://img.shields.io/packagist/dt/swiftmade/playback.svg?style=flat-square)](https://packagist.org/packages/swiftmade/playback) -Do you need idempotent endpoints in Laravel? This package handles just that. +_Idempotent endpoints in Laravel ร  la Stripe._ -What's even idempotency? What should you care? -- https://stripe.com/docs/api/idempotent_requests +Playback gives you idempotent endpoints in Laravel, using Redis locks. [What's even idempotency, and why should I care?](https://stripe.com/docs/api/idempotent_requests) ## Features -- Apply it to a single route, or apply to your whole API... -- Works only for POST requests. Other endpoints are ignored. -- Smart enough to verify path + headers + body is identical before returning the response. -- Will record and play back 2xx and 5xx responses, without touching your controller again. -- Doesn't remember the response if there was a validation error (4xx). So it's safe to retry. -- Prevents race conditions using Laravel's support for cache locks. +- ๐Ÿ“ผ Records and plays back 2xx and 5xx responses, without running your controller code again. +- ๐Ÿ” Built-in validation to prevent attacks by stolen/guessed idempotency keys. +- โš ๏ธ Won't store the response if there was a validation error (4xx). +- ๐ŸŽ Prevents race conditions using atomical Redis locks. ## Installation -> ๐Ÿ’ก Currently, we only support Laravel 8.x. +> ๐Ÿ’ก Supports Laravel 8.x, Laravel 9.x on PHP 7.4, 8.0 or 8.1 1. You can install the package via composer: @@ -45,7 +42,7 @@ Open `config/cache.php` and add a new store. 'driver' => 'redis', // ๐Ÿ‘‡๐Ÿป Caution! // You probably don't want to use the cache connection in production. - // Playback cache can grow to a huge size for busy applications. + // Playback cache can grow to a big size for busy applications. // Make sure your redis instance is ready. 'connection' => 'cache', ], @@ -56,7 +53,7 @@ Open `config/cache.php` and add a new store. Just apply the `Swiftmade\Playback\Playback` middleware to your endpoints. There are many ways of doing it, so here's a link to the docs: -- https://laravel.com/docs/8.x/middleware +- https://laravel.com/docs/9.x/middleware ## Use @@ -72,17 +69,17 @@ If the key is not found during the lookup, a race begins. The first request to a #### Errors: -+ **400 Bad Request** -If you get back status `400`, it means your request was not identical to the cached one. It's the client's responsibility to repeat the exact same request. This is also why another user can't steal a response just by stealing/guessing the idempotency key. The cookies/authentication token would be different, which fails the signature check. +- **400 Bad Request** + If you get back status `400`, it means your request was not identical to the cached one. It's the client's responsibility to repeat the exact same request. This is also why another user can't steal a response just by stealing/guessing the idempotency key. The cookies/authentication token would be different, which fails the signature check. -+ **425 Too Early** -If you get this error, it means you retried too fast after your initial attempt. Don't panic and try again a second later or so. It's perfectly safe to do so! +- **425 Too Early** + If you get this error, it means you retried too fast after your initial attempt. Don't panic and try again a second later or so. It's perfectly safe to do so! ๐Ÿšจ Pro tip: If your controller action returns 4xx or 3xx status code, Playback won't cache the response. It's your responsibility to ensure no side effects take place (or they are rolled back) if a validation fails, a related db record was not found, etc and therefore the response status is 4xx or 3xx. ### Testing -``` bash +```bash composer test ``` @@ -100,9 +97,9 @@ If you discover any security related issues, please email hello@swiftmade.co ins ## Credits -- [Ahmet ร–zisik](https://github.com/swiftmade) -- [All Contributors](../../contributors) +- [Ahmet ร–zisik](https://github.com/swiftmade) +- [All Contributors](../../contributors) ## License -The MIT License (MIT). Please see [License File](LICENSE.md) for more information. \ No newline at end of file +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/composer.json b/composer.json index 944f3ca..96cae63 100644 --- a/composer.json +++ b/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": "^7.3|^8.0", - "illuminate/support": "^8.0" + "illuminate/support": "^8.0|^9.0" }, "require-dev": { - "orchestra/testbench": "^6.0", + "orchestra/testbench": "^6.0|^7.0", "phpunit/phpunit": "^9.0", "spatie/async": "^1.5" }, From 3b2de4c1f46eee2b73219b9b3d6369f247c5aea3 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:24:08 +0300 Subject: [PATCH 06/12] remove L7 from github actions --- .github/workflows/test.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e9cd2ed..f073172 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,24 +18,16 @@ jobs: # os: [ubuntu-latest, windows-latest] os: [ubuntu-latest] php: [7.4, 8.0, 8.1] - laravel: [7.*, 8.*, 9.*] + laravel: [8.*, 9.*] #stability: [prefer-lowest, prefer-stable] stability: [prefer-stable] include: - - laravel: 7.* - testbench: 5.* - laravel: 8.* testbench: ^6.6 - laravel: 9.* testbench: 7.* # Laravel 7 has no support for PHP 8.1 exclude: - - laravel: 7.* - php: 8.1 - # Older versions of L7 doesn't support PHP 8 - - laravel: 7.* - php: 8 - stability: prefer-lowest # Laravel 9 doesn't support < PHP 8 - laravel: 9.* php: 7.4 From 68b695377f06ba29bc895117d863c9f2ce01f2c6 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:27:42 +0300 Subject: [PATCH 07/12] add redis to github actions --- .github/workflows/test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f073172..f37179e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,9 @@ on: jobs: test: + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} + strategy: fail-fast: true matrix: @@ -32,7 +34,12 @@ jobs: - laravel: 9.* php: 7.4 - name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + services: + redis: + image: redis + ports: + - 6379/tcp + options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - name: Checkout code From 81f36ea50d25a013e219cf50caa3ca0bab7026c9 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:31:25 +0300 Subject: [PATCH 08/12] forward redis port --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f37179e..22fe8e3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: redis: image: redis ports: - - 6379/tcp + - 6379:6379 options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: From 90d51e9b0df21a2126e3516fd2f1c5c7a0af16a0 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 29 Jul 2022 17:34:16 +0300 Subject: [PATCH 09/12] upgrade php cs fixer to v3 --- .gitignore | 3 +- .php-cs-fixer.php | 43 ++++++++++++ .php_cs | 145 --------------------------------------- .php_cs.cache | 1 - composer.json | 3 +- tests/Support/routes.php | 1 + 6 files changed, 48 insertions(+), 148 deletions(-) create mode 100644 .php-cs-fixer.php delete mode 100644 .php_cs delete mode 100644 .php_cs.cache diff --git a/.gitignore b/.gitignore index 3c9ced8..2966620 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ docs vendor coverage laravel.log -.phpunit.result.cache \ No newline at end of file +.phpunit.result.cache +.php-cs-fixer.cache \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..9969620 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,43 @@ +notPath('vendor') + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + __DIR__ . '/config', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +$config = new PhpCsFixer\Config(); + +return $config->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'length'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'class_attributes_separation' => [ + 'elements' => [ + 'method' => 'one', + ], + ], + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + 'single_trait_insert_per_statement' => true, +]) + ->setFinder($finder); diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 172c476..0000000 --- a/.php_cs +++ /dev/null @@ -1,145 +0,0 @@ - ['syntax' => 'short'], - 'binary_operator_spaces' => [ - 'default' => 'single_space', - 'operators' => ['=>' => null], - ], - 'blank_line_after_namespace' => true, - 'blank_line_after_opening_tag' => true, - 'blank_line_before_statement' => [ - 'statements' => ['return'], - ], - 'braces' => true, - 'cast_spaces' => true, - 'class_attributes_separation' => [ - 'elements' => ['method'], - ], - 'class_definition' => true, - 'concat_space' => [ - 'spacing' => 'one', - ], - 'declare_equal_normalize' => true, - 'elseif' => true, - 'encoding' => true, - 'full_opening_tag' => true, - 'fully_qualified_strict_types' => true, // added by Shift - 'function_declaration' => true, - 'function_typehint_space' => true, - 'heredoc_to_nowdoc' => true, - 'include' => true, - 'increment_style' => ['style' => 'post'], - 'indentation_type' => true, - 'linebreak_after_opening_tag' => true, - 'line_ending' => true, - 'lowercase_cast' => true, - 'lowercase_constants' => true, - 'lowercase_keywords' => true, - 'lowercase_static_reference' => true, // added from Symfony - 'magic_method_casing' => true, // added from Symfony - 'magic_constant_casing' => true, - 'method_argument_space' => true, - 'native_function_casing' => true, - 'no_alias_functions' => true, - 'no_extra_blank_lines' => [ - 'tokens' => [ - 'extra', - 'throw', - 'use', - 'use_trait', - ], - ], - 'no_blank_lines_after_class_opening' => true, - 'no_blank_lines_after_phpdoc' => true, - 'no_closing_tag' => true, - 'no_empty_phpdoc' => true, - 'no_empty_statement' => true, - 'no_leading_import_slash' => true, - 'no_leading_namespace_whitespace' => true, - 'no_mixed_echo_print' => [ - 'use' => 'echo', - ], - 'no_multiline_whitespace_around_double_arrow' => true, - 'multiline_whitespace_before_semicolons' => [ - 'strategy' => 'no_multi_line', - ], - 'no_short_bool_cast' => true, - 'no_singleline_whitespace_before_semicolons' => true, - 'no_spaces_after_function_name' => true, - 'no_spaces_around_offset' => true, - 'no_spaces_inside_parenthesis' => true, - 'no_trailing_comma_in_list_call' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'no_trailing_whitespace' => true, - 'no_trailing_whitespace_in_comment' => true, - 'no_unneeded_control_parentheses' => true, - 'no_unreachable_default_argument_value' => true, - 'no_useless_return' => true, - 'no_whitespace_before_comma_in_array' => true, - 'no_whitespace_in_blank_line' => true, - 'normalize_index_brace' => true, - 'not_operator_with_successor_space' => true, - 'object_operator_without_whitespace' => true, - 'ordered_imports' => ['sortAlgorithm' => 'length'], - 'phpdoc_indent' => true, - 'phpdoc_inline_tag' => true, - 'phpdoc_no_access' => true, - 'phpdoc_no_package' => true, - 'phpdoc_no_useless_inheritdoc' => true, - 'phpdoc_scalar' => true, - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_summary' => true, - 'phpdoc_to_comment' => true, - 'phpdoc_trim' => true, - 'phpdoc_types' => true, - 'phpdoc_var_without_name' => true, - 'psr4' => true, - 'self_accessor' => true, - 'short_scalar_cast' => true, - 'simplified_null_return' => true, - 'single_blank_line_at_eof' => true, - 'single_blank_line_before_namespace' => true, - 'single_class_element_per_statement' => true, - 'single_import_per_statement' => true, - 'single_line_after_imports' => true, - 'single_line_comment_style' => [ - 'comment_types' => ['hash'], - ], - 'single_quote' => true, - 'space_after_semicolon' => true, - 'standardize_not_equals' => true, - 'switch_case_semicolon_to_colon' => true, - 'switch_case_space' => true, - 'ternary_operator_spaces' => true, - 'trailing_comma_in_multiline_array' => true, - 'trim_array_spaces' => true, - 'unary_operator_spaces' => true, - 'visibility_required' => [ - 'elements' => ['method', 'property'], - ], - 'whitespace_after_comma_in_array' => true, - 'no_unused_imports' => true, -]; - -$finder = Finder::create() - ->notPath('bootstrap') - ->notPath('storage') - ->notPath('vendor') - ->notPath('spark') - ->in(getcwd()) - ->name('*.php') - ->notName('*.blade.php') - ->notName('index.php') - ->notName('server.php') - ->ignoreDotFiles(true) - ->ignoreVCS(true); - -return Config::create() - ->setFinder($finder) - ->setRules($rules) - ->setRiskyAllowed(true) - ->setUsingCache(true); diff --git a/.php_cs.cache b/.php_cs.cache deleted file mode 100644 index a7f32f5..0000000 --- a/.php_cs.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"7.4.16","version":"2.17.3:v2.17.3#bd32f5dd72cdfc7b53f54077f980e144bfa2f595","indent":" ","lineEnding":"\n","rules":{"array_syntax":{"syntax":"short"},"binary_operator_spaces":{"default":"single_space","operators":{"=>":null}},"blank_line_after_namespace":true,"blank_line_after_opening_tag":true,"blank_line_before_statement":{"statements":["return"]},"braces":true,"cast_spaces":true,"class_attributes_separation":{"elements":["method"]},"class_definition":true,"concat_space":{"spacing":"one"},"declare_equal_normalize":true,"elseif":true,"encoding":true,"full_opening_tag":true,"fully_qualified_strict_types":true,"function_declaration":true,"function_typehint_space":true,"heredoc_to_nowdoc":true,"include":true,"increment_style":{"style":"post"},"indentation_type":true,"linebreak_after_opening_tag":true,"line_ending":true,"lowercase_cast":true,"lowercase_constants":true,"lowercase_keywords":true,"lowercase_static_reference":true,"magic_method_casing":true,"magic_constant_casing":true,"method_argument_space":true,"native_function_casing":true,"no_alias_functions":true,"no_extra_blank_lines":{"tokens":["extra","throw","use","use_trait"]},"no_blank_lines_after_class_opening":true,"no_blank_lines_after_phpdoc":true,"no_closing_tag":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_import_slash":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":{"use":"echo"},"no_multiline_whitespace_around_double_arrow":true,"multiline_whitespace_before_semicolons":{"strategy":"no_multi_line"},"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_after_function_name":true,"no_spaces_around_offset":true,"no_spaces_inside_parenthesis":true,"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"no_unneeded_control_parentheses":true,"no_unreachable_default_argument_value":true,"no_useless_return":true,"no_whitespace_before_comma_in_array":true,"no_whitespace_in_blank_line":true,"normalize_index_brace":true,"not_operator_with_successor_space":true,"object_operator_without_whitespace":true,"ordered_imports":{"sortAlgorithm":"length"},"phpdoc_indent":true,"phpdoc_inline_tag":true,"phpdoc_no_access":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_types":true,"phpdoc_var_without_name":true,"psr4":true,"self_accessor":true,"short_scalar_cast":true,"simplified_null_return":true,"single_blank_line_at_eof":true,"single_blank_line_before_namespace":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"single_line_comment_style":{"comment_types":["hash"]},"single_quote":true,"space_after_semicolon":true,"standardize_not_equals":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"ternary_operator_spaces":true,"trailing_comma_in_multiline_array":true,"trim_array_spaces":true,"unary_operator_spaces":true,"visibility_required":{"elements":["method","property"]},"whitespace_after_comma_in_array":true,"no_unused_imports":true},"hashes":{"config\/config.php":1710567645,"tests\/Support\/TestServiceProvider.php":1054953203,"tests\/Support\/routes.php":2367305398,"tests\/PlaybackTest.php":3239734766,"src\/Recorder.php":3485823478,"src\/RecordedResponse.php":2846671540,"src\/PlaybackServiceProvider.php":2838178533,"src\/Playback.php":3212455048}} \ No newline at end of file diff --git a/composer.json b/composer.json index 96cae63..8bb5c06 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "illuminate/support": "^8.0|^9.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.9", "orchestra/testbench": "^6.0|^7.0", "phpunit/phpunit": "^9.0", "spatie/async": "^1.5" @@ -48,4 +49,4 @@ ] } } -} \ No newline at end of file +} diff --git a/tests/Support/routes.php b/tests/Support/routes.php index 7ccf8a9..b72eb0d 100644 --- a/tests/Support/routes.php +++ b/tests/Support/routes.php @@ -2,6 +2,7 @@ use Illuminate\Http\Request; use Swiftmade\Playback\Playback; +use Illuminate\Support\Facades\Route; Route::get('get', function () { return 'Get response ' . microtime(); From 1e427ffbe602288a8c7d8ae8507b37bccfc095b4 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 3 Jul 2026 17:24:24 +0300 Subject: [PATCH 10/12] update --- .github/workflows/test.yml | 44 +++++++++++++++++++++-------------- CHANGELOG.md | 8 ++++++- README.md | 4 ++-- composer.json | 12 +++++----- phpunit.xml.dist | 15 +++++++----- tests/PlaybackTest.php | 47 ++++++++++++-------------------------- 6 files changed, 65 insertions(+), 65 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22fe8e3..5a5783c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,27 +12,37 @@ jobs: test: name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} - + strategy: fail-fast: true matrix: - # Disable testing on windows for now... - # os: [ubuntu-latest, windows-latest] os: [ubuntu-latest] - php: [7.4, 8.0, 8.1] - laravel: [8.*, 9.*] - #stability: [prefer-lowest, prefer-stable] + php: ['8.1', '8.2', '8.3', '8.4'] + laravel: [10.*, 11.*, 12.*, 13.*] stability: [prefer-stable] include: - - laravel: 8.* - testbench: ^6.6 - - laravel: 9.* - testbench: 7.* - # Laravel 7 has no support for PHP 8.1 + - laravel: 10.* + testbench: 8.* + - laravel: 11.* + testbench: 9.* + - laravel: 12.* + testbench: 10.* + - laravel: 13.* + testbench: 11.* exclude: - # Laravel 9 doesn't support < PHP 8 - - laravel: 9.* - php: 7.4 + # Laravel 10 supports PHP 8.1 - 8.3 + - laravel: 10.* + php: '8.4' + # Laravel 11 & 12 require PHP >= 8.2 + - laravel: 11.* + php: '8.1' + - laravel: 12.* + php: '8.1' + # Laravel 13 requires PHP >= 8.3 + - laravel: 13.* + php: '8.1' + - laravel: 13.* + php: '8.2' services: redis: @@ -43,13 +53,13 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo, redis coverage: none - name: Setup problem matchers @@ -61,4 +71,4 @@ jobs: composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update composer update --${{ matrix.stability }} --prefer-dist --no-interaction - name: Execute tests - run: vendor/bin/phpunit \ No newline at end of file + run: vendor/bin/phpunit diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f50957..852f540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,4 +11,10 @@ All notable changes to `swiftmade/playback` will be documented in this file - Fix lock key - Add a test case for the handling of race condition - Minor refactor and documentation improvements -- Remove predis dependency \ No newline at end of file +- Remove predis dependency + +## Unreleased + +- Add support for Laravel 10, 11, 12 and 13 +- Drop support for Laravel 8 and 9 (and PHP < 8.1) +- Upgrade test suite to PHPUnit 10/11/12 \ No newline at end of file diff --git a/README.md b/README.md index 4bc609a..6432d0f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Playback gives you idempotent endpoints in Laravel, using Redis locks. [What's e ## Installation -> ๐Ÿ’ก Supports Laravel 8.x, Laravel 9.x on PHP 7.4, 8.0 or 8.1 +> ๐Ÿ’ก Supports Laravel 10.x, 11.x, 12.x and 13.x on PHP 8.1 โ€“ 8.4 1. You can install the package via composer: @@ -53,7 +53,7 @@ Open `config/cache.php` and add a new store. Just apply the `Swiftmade\Playback\Playback` middleware to your endpoints. There are many ways of doing it, so here's a link to the docs: -- https://laravel.com/docs/9.x/middleware +- https://laravel.com/docs/middleware ## Use diff --git a/composer.json b/composer.json index 8bb5c06..2952347 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/support": "^8.0|^9.0" + "php": "^8.1", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.9", - "orchestra/testbench": "^6.0|^7.0", - "phpunit/phpunit": "^9.0", - "spatie/async": "^1.5" + "friendsofphp/php-cs-fixer": "^3.64", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^10.5|^11.0|^12.0", + "spatie/async": "^1.6" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2dd1d48..e5ad4b5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,12 +1,10 @@ - @@ -14,4 +12,9 @@ tests + + + src + + diff --git a/tests/PlaybackTest.php b/tests/PlaybackTest.php index de596e5..fa5cb48 100644 --- a/tests/PlaybackTest.php +++ b/tests/PlaybackTest.php @@ -5,6 +5,7 @@ use Spatie\Async\Pool; use Swiftmade\Playback\Recorder; use Orchestra\Testbench\TestCase; +use PHPUnit\Framework\Attributes\Test; use Swiftmade\Playback\PlaybackServiceProvider; use Swiftmade\Playback\Tests\Support\TestServiceProvider; @@ -24,9 +25,7 @@ protected function tearDown(): void parent::tearDown(); } - /** - * @test - */ + #[Test] public function it_plays_back_post_requests() { $headers = [ @@ -55,9 +54,7 @@ public function it_plays_back_post_requests() ); } - /** - * @test - */ + #[Test] public function it_plays_back_internal_server_errors() { $headers = [ @@ -85,9 +82,7 @@ public function it_plays_back_internal_server_errors() ); } - /** - * @test - */ + #[Test] public function different_key_returns_different_response() { $headers = [ @@ -116,9 +111,7 @@ public function different_key_returns_different_response() ); } - /** - * @test - */ + #[Test] public function it_returns_400_if_headers_change() { $headers = [ @@ -137,9 +130,7 @@ public function it_returns_400_if_headers_change() $response2->assertStatus(400); } - /** - * @test - */ + #[Test] public function it_returns_400_if_body_changes() { $headers = [ @@ -158,9 +149,7 @@ public function it_returns_400_if_body_changes() $response2->assertStatus(400); } - /** - * @test - */ + #[Test] public function it_returns_400_if_query_parameters_change() { $headers = [ @@ -176,9 +165,7 @@ public function it_returns_400_if_query_parameters_change() $response2->assertStatus(400); } - /** - * @test - */ + #[Test] public function it_returns_400_if_path_changes() { $headers = [ @@ -194,9 +181,7 @@ public function it_returns_400_if_path_changes() $response2->assertStatus(400); } - /** - * @test - */ + #[Test] public function it_does_not_play_back_if_validation_fails() { $headers = [ @@ -228,9 +213,7 @@ public function it_does_not_play_back_if_validation_fails() $response4->assertHeader(config('playback.playback_header_name'), 'validation_test'); } - /** - * @test - */ + #[Test] public function it_does_not_record_get_request() { $headers = [ @@ -253,9 +236,7 @@ public function it_does_not_record_get_request() ); } - /** - * @test - */ + #[Test] public function the_first_request_wins() { $headers = [ @@ -267,7 +248,7 @@ public function the_first_request_wins() ->timeout(6); $pool[] = async(function () use ($headers) { - $app = new self(); + $app = new self('the_first_request_wins'); $app->setUpBeforeClass(); $app->setUp(); $response = $app->post('slow', [], $headers); @@ -285,7 +266,7 @@ public function the_first_request_wins() $pool[] = async(function () use ($headers) { usleep(150 * 1000); - $app = new self(); + $app = new self('the_first_request_wins'); $app->setUpBeforeClass(); $app->setUp(); $response = $app->post('slow', [], $headers); @@ -302,7 +283,7 @@ public function the_first_request_wins() $pool[] = async(function () use ($headers) { usleep(3000 * 1000); - $app = new self(); + $app = new self('the_first_request_wins'); $app->setUpBeforeClass(); $app->setUp(); $response = $app->post('slow', [], $headers); From e7c03f0bcd8bd20bb0b7d7102e077debbbf0762e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=96z=C4=B1=C5=9F=C4=B1k?= Date: Fri, 3 Jul 2026 17:25:54 +0300 Subject: [PATCH 11/12] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34695fc..3b88630 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,9 @@ on: branches: - master +permissions: + contents: read + jobs: test: name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} From feb95baf6fcc0434d8c0b77d38f3d40b29bd59a2 Mon Sep 17 00:00:00 2001 From: Ahmet Ozisik Date: Fri, 3 Jul 2026 17:37:33 +0300 Subject: [PATCH 12/12] update --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34695fc..1ec0a0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,7 +68,12 @@ jobs: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + # We intentionally test older, still-constraint-compatible Laravel releases + # (e.g. L10 is EOL) that may carry open Packagist advisories. Composer 2.9+ + # blocks advisory-flagged versions during resolution by default, so allow them + # here. This only affects the CI runner, not the published composer.json. + composer config audit.block-insecure false + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --dev --no-interaction --no-update composer update --${{ matrix.stability }} --prefer-dist --no-interaction - name: Execute tests run: vendor/bin/phpunit