diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7b1ab8..db619c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Coverage +name: Transloadit PHP SDK CI on: push: branches: @@ -10,29 +10,56 @@ on: jobs: ci: runs-on: ubuntu-latest + strategy: + fail-fast: true + max-parallel: 1 + matrix: + php: + - 8.1 + - 8.2 + dependencies: + - locked + - lowest + - highest + name: PHP ${{ matrix.php }} - ${{ matrix.dependencies }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install tsx + run: npm install -g tsx - uses: shivammathur/setup-php@v2 - # https://github.com/boyney123/github-actions/blob/HEAD/src/actions/setup-php.md with: - php-version: '8.1' + php-version: ${{ matrix.php }} tools: php-cs-fixer, phpunit - - uses: ramsey/composer-install@v2 + coverage: ${{ matrix.php == '8.1' && matrix.dependencies == 'locked' && 'xdebug' || 'none' }} + - uses: ramsey/composer-install@v3 with: - dependency-versions: locked + dependency-versions: ${{ matrix.dependencies }} composer-options: '--ignore-platform-reqs' - - name: Test + - name: Test with Coverage + if: matrix.php == '8.1' && matrix.dependencies == 'locked' run: | make test-all-coverage env: - TEST_ACCOUNT_KEY: ${{secrets.TEST_ACCOUNT_KEY}} - TEST_ACCOUNT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}} - - uses: codecov/codecov-action@v2 + TRANSLOADIT_KEY: ${{secrets.TEST_ACCOUNT_KEY}} + TRANSLOADIT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}} + TEST_NODE_PARITY: 1 + - name: Test without Coverage + if: matrix.php != '8.1' || matrix.dependencies != 'locked' + run: | + make test-all + env: + TRANSLOADIT_KEY: ${{secrets.TEST_ACCOUNT_KEY}} + TRANSLOADIT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}} + TEST_NODE_PARITY: 1 + - name: Publish Coverage Report + if: github.event_name == 'pull_request' && matrix.php == '8.1' && matrix.dependencies == 'locked' + uses: lucassabreu/comment-coverage-clover@v0.13.0 with: - files: ./build/logs/clover.xml - fail_ci_if_error: true - target: 90% # Desired coverage percentage - threshold: 2% # Allowed coverage percentage deviation - verbose: true + file: ./build/logs/clover.xml + with-table: true + with-chart: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 39a702b..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Tests -on: - push: - branches: - - main - pull_request: - types: - - opened - - synchronize -jobs: - tests: - runs-on: ubuntu-latest - strategy: - fail-fast: true - max-parallel: 1 - matrix: - php: - - 8.1 - - 8.2 - dependencies: - - locked - - lowest - - highest - name: Tests on PHP ${{ matrix.php }} - ${{ matrix.dependencies }} - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - uses: shivammathur/setup-php@v2 - # https://github.com/boyney123/github-actions/blob/HEAD/src/actions/setup-php.md - with: - php-version: ${{ matrix.php }} - coverage: none - - uses: ramsey/composer-install@v2 - with: - dependency-versions: '${{ matrix.dependencies }}' - - name: Test - run: | - make test-all - env: - TEST_ACCOUNT_KEY: ${{secrets.TEST_ACCOUNT_KEY}} - TEST_ACCOUNT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}} diff --git a/.vscode/php-sdk.code-workspace b/.vscode/php-sdk.code-workspace index 64bfb87..05313d0 100644 --- a/.vscode/php-sdk.code-workspace +++ b/.vscode/php-sdk.code-workspace @@ -4,5 +4,10 @@ "path": ".." } ], - "settings": {} + "settings": { + "workbench.colorCustomizations": { + "titleBar.activeForeground": "#232531", + "titleBar.activeBackground": "#8993be" + }, + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da5a35..41852b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ### [main](https://github.com/transloadit/php-sdk/tree/main) -diff: https://github.com/transloadit/php-sdk/compare/3.1.0...main +diff: https://github.com/transloadit/php-sdk/compare/3.2.0...main + +### [3.2.0](https://github.com/transloadit/php-sdk/tree/3.2.0) + +- Implement `signedSmartCDNUrl` + +diff: https://github.com/transloadit/php-sdk/compare/3.1.0...3.2.0 ### [3.1.0](https://github.com/transloadit/php-sdk/tree/3.1.0) diff --git a/Makefile b/Makefile index a0d09e9..52f9eae 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ SHELL := /usr/bin/env bash export PATH := $(PATH):bin -phpUnit = vendor/phpunit/phpunit/phpunit --colors --verbose --stderr --configuration phpunit.xml $(2) $(1) +phpUnit = vendor/bin/phpunit --colors --verbose --stderr --configuration phpunit.xml $(2) $(1) .PHONY: install install: diff --git a/README.md b/README.md index 3ea2463..543cc86 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ require 'vendor/autoload.php'; use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->createAssembly([ @@ -90,8 +90,8 @@ require 'vendor/autoload.php'; use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); // Check if this request is a Transloadit redirect_url notification. @@ -139,7 +139,10 @@ To integrate Uppy with your PHP backend: 1. Include Uppy in your HTML: ```html - + ``` @@ -156,8 +159,8 @@ To integrate Uppy with your PHP backend: }) .use(Uppy.Transloadit, { params: { - auth: { key: 'YOUR_TRANSLOADIT_KEY' }, - template_id: 'YOUR_TEMPLATE_ID', + auth: { key: 'MY_TRANSLOADIT_KEY' }, + template_id: 'MY_TEMPLATE_ID', notify_url: 'https://your-site.com/transloadit_notify.php' } }) @@ -179,8 +182,8 @@ require 'vendor/autoload.php'; use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = Transloadit::response(); @@ -188,17 +191,17 @@ if ($response) { // Process the assembly result $assemblyId = $response->data['assembly_id']; $assemblyStatus = $response->data['ok']; - + // You can store the assembly information in your database // or perform any other necessary actions here - + // Log the response for debugging error_log('Transloadit Assembly Completed: ' . $assemblyId); error_log('Assembly Status: ' . ($assemblyStatus ? 'Success' : 'Failed')); - + // Optionally, you can write the response to a file file_put_contents('transloadit_response_' . $assemblyId . '.json', json_encode($response->data)); - + // Send a 200 OK response to Transloadit http_response_code(200); echo 'OK'; @@ -221,11 +224,11 @@ You can use the `getAssembly` method to get the Assembly Status. ```php 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->getAssembly($assemblyId); @@ -251,14 +254,14 @@ require 'vendor/autoload.php'; use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->createAssembly([ 'files' => ['/PATH/TO/FILE.jpg'], 'params' => [ - 'template_id' => 'YOUR_TEMPLATE_ID', + 'template_id' => 'MY_TEMPLATE_ID', ], ]); @@ -269,12 +272,57 @@ echo ''; ``` - - -### Signature Auth +### Signature Auth (Assemblies) Signature Authentication is done by the PHP SDK by default internally so you do not need to worry about this :) +### Signature Auth (Smart CDN) + +You can use the `signedSmartCDNUrl` method to generate signed URLs for Transloadit's [Smart CDN](https://transloadit.com/services/content-delivery/): + +```php + 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', +]); + +// Basic usage +$url = $transloadit->signedSmartCDNUrl( + 'your-workspace-slug', + 'your-template-slug', + 'avatars/jane.jpg' +); + +// Advanced usage with custom parameters and expiry +$url = $transloadit->signedSmartCDNUrl( + 'your-workspace-slug', + 'your-template-slug', + 'avatars/jane.jpg', + ['width' => 100, 'height' => 100], // Additional parameters + 1732550672867, // Expiry date in milliseconds since epoch +); + +echo $url; +``` + +The generated URL will be in the format: + +``` +https://{workspace-slug}.tlcdn.com/{template-slug}/{input-field}?{query-params}&sig=sha256:{signature} +``` + +Note that: + +- The URL will expire after the specified time (default: 1 hour) +- All parameters are properly encoded +- The signature is generated using HMAC SHA-256 +- Query parameters are sorted alphabetically before signing + ## Example For fully working examples take a look at [`examples/`](https://github.com/transloadit/php-sdk/tree/HEAD/examples). @@ -480,7 +528,57 @@ Feel free to fork this project. We will happily merge bug fixes or other small improvements. For bigger changes you should probably get in touch with us before you start to avoid not seeing them merged. -## Versioning +### Testing + +#### Basic Tests + +```bash +make test +``` + +#### System Tests + +System tests require: + +1. Valid Transloadit credentials in environment: + +```bash +export TRANSLOADIT_KEY='your-auth-key' +export TRANSLOADIT_SECRET='your-auth-secret' +``` + +Then run: + +```bash +make test-all +``` + +#### Node.js Reference Implementation Parity Assertions + +The SDK includes assertions that compare URL signing with our reference Node.js implementation. To run these tests: + +1. Requirements: + + - Node.js installed + - tsx installed globally (`npm install -g tsx`) + +2. Install dependencies: + +```bash +npm install -g tsx +``` + +3. Run the test: + +```bash +export TRANSLOADIT_KEY='your-auth-key' +export TRANSLOADIT_SECRET='your-auth-secret' +TEST_NODE_PARITY=1 make test-all +``` + +CI opts-into `TEST_NODE_PARITY=1`, and you can optionally do this locally as well. + +### Versioning This project implements the Semantic Versioning guidelines. @@ -496,7 +594,7 @@ And constructed with the following guidelines: For more information on SemVer, please visit http://semver.org/. -## Releasing a new version +### Releasing a new version ```bash # 1. update CHANGELOG.md diff --git a/composer.lock b/composer.lock index 361e616..50cfa22 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "72da3fd831539873162b58b1096b6e20", + "content-hash": "87ef0b624245fdf2bf16493b6173595a", "packages": [], "packages-dev": [ { @@ -79,22 +79,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", + "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" @@ -103,10 +103,11 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "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": { @@ -119,9 +120,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -187,7 +185,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -203,38 +201,37 @@ "type": "tidelift" } ], - "time": "2022-08-28T15:39:27+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -271,7 +268,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -287,26 +284,26 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.4", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", + "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.0", + "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0" }, "provide": { @@ -314,9 +311,9 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "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" @@ -326,9 +323,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -390,7 +384,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.4" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -406,20 +400,20 @@ "type": "tidelift" } ], - "time": "2023-03-09T13:19:02+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -427,11 +421,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "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", @@ -457,7 +452,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -465,29 +460,31 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -495,7 +492,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -519,26 +516,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "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", @@ -579,9 +577,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -636,28 +640,28 @@ }, { "name": "php-coveralls/php-coveralls", - "version": "v2.5.3", + "version": "v2.7.0", "source": { "type": "git", "url": "https://github.com/php-coveralls/php-coveralls.git", - "reference": "9d8243bbf0e053333692857c98fab7cfba0d60a9" + "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/9d8243bbf0e053333692857c98fab7cfba0d60a9", - "reference": "9d8243bbf0e053333692857c98fab7cfba0d60a9", + "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/b36fa4394e519dafaddc04ae03976bc65a25ba15", + "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15", "shasum": "" }, "require": { "ext-json": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.0 || ^7.0", - "php": "^5.5 || ^7.0 || ^8.0", + "php": "^7.0 || ^8.0", "psr/log": "^1.0 || ^2.0", - "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0", - "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0", - "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0", - "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || >=8.0 <8.5.29 || >=9.0 <9.5.23", @@ -713,41 +717,41 @@ ], "support": { "issues": "https://github.com/php-coveralls/php-coveralls/issues", - "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.5.3" + "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.7.0" }, - "time": "2022-09-12T20:47:09+00:00" + "time": "2023-11-22T10:21:01+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.26", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -756,7 +760,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -784,7 +788,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -792,7 +797,7 @@ "type": "github" } ], - "time": "2023-03-06T12:58:08+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1037,45 +1042,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.5", + "version": "9.6.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5" + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5", - "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -1119,7 +1124,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" }, "funding": [ { @@ -1135,7 +1141,7 @@ "type": "tidelift" } ], - "time": "2023-03-09T06:34:10+00:00" + "time": "2024-09-19T10:50:18+00:00" }, { "name": "psr/container", @@ -1192,21 +1198,21 @@ }, { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "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" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -1226,7 +1232,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -1238,27 +1244,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -1278,10 +1284,10 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -1293,31 +1299,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1332,7 +1338,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -1346,9 +1352,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "psr/log", @@ -1446,16 +1452,16 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -1490,7 +1496,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -1498,7 +1504,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -1687,20 +1693,20 @@ }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1732,7 +1738,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -1740,20 +1746,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -1798,7 +1804,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -1806,7 +1812,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", @@ -1873,16 +1879,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -1938,7 +1944,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -1946,20 +1952,20 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -2002,7 +2008,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -2010,24 +2016,24 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -2059,7 +2065,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -2067,7 +2073,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", @@ -2246,16 +2252,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -2267,7 +2273,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2288,8 +2294,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -2297,7 +2302,7 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", @@ -2410,16 +2415,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.2", + "version": "3.11.1", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", "shasum": "" }, "require": { @@ -2429,11 +2434,11 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", "extra": { @@ -2448,55 +2453,76 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "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/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "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" }, - "time": "2023-02-22T23:07:41+00:00" + "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-11-16T12:02:36+00:00" }, { "name": "symfony/config", - "version": "v6.2.7", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "249271da6f545d6579e0663374f8249a80be2893" + "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/249271da6f545d6579e0663374f8249a80be2893", - "reference": "249271da6f545d6579e0663374f8249a80be2893", + "url": "https://api.github.com/repos/symfony/config/zipball/4e55e7e4ffddd343671ea972216d4509f46c22ef", + "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0|^7.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<5.4" + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2524,7 +2550,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.2.7" + "source": "https://github.com/symfony/config/tree/v6.4.14" }, "funding": [ { @@ -2540,28 +2566,28 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-11-04T11:33:53+00:00" }, { "name": "symfony/console", - "version": "v6.2.7", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45" + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45", + "url": "https://api.github.com/repos/symfony/console/zipball/f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -2575,18 +2601,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2615,12 +2639,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.7" + "source": "https://github.com/symfony/console/tree/v6.4.15" }, "funding": [ { @@ -2636,20 +2660,20 @@ "type": "tidelift" } ], - "time": "2023-02-25T17:00:03+00:00" + "time": "2024-11-06T14:19:14+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -2658,7 +2682,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2687,7 +2711,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -2703,20 +2727,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { @@ -2724,6 +2748,9 @@ "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -2750,7 +2777,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -2766,24 +2793,24 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2793,9 +2820,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2832,7 +2856,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -2848,33 +2872,30 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2913,7 +2934,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -2929,33 +2950,30 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2997,7 +3015,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -3013,24 +3031,24 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3040,9 +3058,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3080,7 +3095,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -3096,36 +3111,34 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3165,7 +3178,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -3181,25 +3194,25 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.2.7", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f" + "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f3adc98c1061875dd2edcd45e5b04e63d0e29f8f", - "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2cae0a6f8d04937d02f6d19806251e2104d54f92", + "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/service-contracts": "^1|^2|^3" + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -3227,7 +3240,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.2.7" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.13" }, "funding": [ { @@ -3243,20 +3256,20 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/string", - "version": "v6.2.7", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d" + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d", + "url": "https://api.github.com/repos/symfony/string/zipball/73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", "shasum": "" }, "require": { @@ -3267,14 +3280,14 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3313,7 +3326,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.7" + "source": "https://github.com/symfony/string/tree/v6.4.15" }, "funding": [ { @@ -3329,34 +3342,32 @@ "type": "tidelift" } ], - "time": "2023-02-24T10:42:00+00:00" + "time": "2024-11-13T13:31:12+00:00" }, { "name": "symfony/yaml", - "version": "v6.2.7", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e8e6a1d59e050525f27a1f530aa9703423cb7f57" + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e8e6a1d59e050525f27a1f530aa9703423cb7f57", - "reference": "e8e6a1d59e050525f27a1f530aa9703423cb7f57", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -3387,7 +3398,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.2.7" + "source": "https://github.com/symfony/yaml/tree/v6.4.13" }, "funding": [ { @@ -3403,20 +3414,20 @@ "type": "tidelift" } ], - "time": "2023-02-16T09:57:23+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -3445,7 +3456,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -3453,17 +3464,17 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.4.0" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/examples/1-assembly-request.php b/examples/1-assembly-request.php index 1385c58..0ac5760 100644 --- a/examples/1-assembly-request.php +++ b/examples/1-assembly-request.php @@ -14,8 +14,8 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->createAssembly([ diff --git a/examples/2-assembly-form.php b/examples/2-assembly-form.php index a3266c0..def0d0e 100644 --- a/examples/2-assembly-form.php +++ b/examples/2-assembly-form.php @@ -20,8 +20,8 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); // Check if this request is a Transloadit redirect_url notification. diff --git a/examples/3-assembly-form-with-jquery-plugin.php b/examples/3-assembly-form-with-jquery-plugin.php index 1a4e477..34c2e4b 100644 --- a/examples/3-assembly-form-with-jquery-plugin.php +++ b/examples/3-assembly-form-with-jquery-plugin.php @@ -13,8 +13,8 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = Transloadit::response(); diff --git a/examples/4-fetch-assembly-status.php b/examples/4-fetch-assembly-status.php index 4f6875b..6b45b4c 100644 --- a/examples/4-fetch-assembly-status.php +++ b/examples/4-fetch-assembly-status.php @@ -7,11 +7,11 @@ You can use the `getAssembly` method to get the Assembly Status. */ -$assemblyId = 'YOUR_ASSEMBLY_ID'; +$assemblyId = 'MY_ASSEMBLY_ID'; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->getAssembly($assemblyId); diff --git a/examples/5-assembly-with-template.php b/examples/5-assembly-with-template.php index 32c8018..3920a39 100644 --- a/examples/5-assembly-with-template.php +++ b/examples/5-assembly-with-template.php @@ -14,14 +14,14 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => 'YOUR_TRANSLOADIT_KEY', - 'secret' => 'YOUR_TRANSLOADIT_SECRET', + 'key' => 'MY_TRANSLOADIT_KEY', + 'secret' => 'MY_TRANSLOADIT_SECRET', ]); $response = $transloadit->createAssembly([ 'files' => [dirname(__FILE__) . '/fixture/straw-apple.jpg'], 'params' => [ - 'template_id' => 'YOUR_TEMPLATE_ID', + 'template_id' => 'MY_TEMPLATE_ID', ], ]); diff --git a/examples/6-assembly-with-timeout.php b/examples/6-assembly-with-timeout.php index fb01b07..0561b71 100644 --- a/examples/6-assembly-with-timeout.php +++ b/examples/6-assembly-with-timeout.php @@ -5,8 +5,8 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => getenv('YOUR_TRANSLOADIT_KEY'), - 'secret' => getenv('YOUR_TRANSLOADIT_SECRET'), + 'key' => getenv('MY_TRANSLOADIT_KEY'), + 'secret' => getenv('MY_TRANSLOADIT_SECRET'), ]); $response = $transloadit->createAssembly([ diff --git a/examples/7-disable-ssl-verification.php b/examples/7-disable-ssl-verification.php index 5438272..3667357 100644 --- a/examples/7-disable-ssl-verification.php +++ b/examples/7-disable-ssl-verification.php @@ -9,8 +9,8 @@ use transloadit\Transloadit; $transloadit = new Transloadit([ - 'key' => getenv('YOUR_TRANSLOADIT_KEY'), - 'secret' => getenv('YOUR_TRANSLOADIT_SECRET'), + 'key' => getenv('MY_TRANSLOADIT_KEY'), + 'secret' => getenv('MY_TRANSLOADIT_SECRET'), ]); $response = $transloadit->createAssembly([ diff --git a/lib/transloadit/Transloadit.php b/lib/transloadit/Transloadit.php index 681f0da..41675b1 100644 --- a/lib/transloadit/Transloadit.php +++ b/lib/transloadit/Transloadit.php @@ -135,4 +135,93 @@ public function cancelAssembly($assembly_id) { return $response; } } + + /** + * Generates a signed URL for Transloadit's Smart CDN + * https://transloadit.com/services/content-delivery/ + * + * @param string $workspaceSlug The workspace slug + * @param string $templateSlug The template slug + * @param string $inputField The input field (optional) + * @param array $params Additional parameters (optional) + * @param int $expireAtMs Number of milliseconds since epoch at which the URL expires + * @return string The signed URL + */ + public function signedSmartCDNUrl( + string $workspaceSlug, + string $templateSlug, + string $inputField = '', + array $params = [], + int $expireAtMs = null + ): string { + // Validate required fields + if (!$workspaceSlug) { + throw new \InvalidArgumentException('workspace is required'); + } + if (!$templateSlug) { + throw new \InvalidArgumentException('template is required'); + } + if ($inputField === null) { + throw new \InvalidArgumentException('input must be a string'); + } + + // Add auth parameters + $queryParams = []; + + // Process params to match Node.js behavior + foreach ($params as $key => $value) { + if (is_array($value)) { + foreach ($value as $val) { + if ($val !== null) { + $queryParams[$key][] = $val; + } + } + } elseif ($value !== null) { + $queryParams[$key] = $value; + } + } + + $queryParams['auth_key'] = $this->key; + $queryParams['exp'] = (string)($expireAtMs ?? (time() * 1000 + 3600000)); // Default 1 hour + + // Sort parameters alphabetically + ksort($queryParams); + + // Build query string manually to match Node.js behavior + $queryParts = []; + foreach ($queryParams as $key => $value) { + if (is_array($value)) { + foreach ($value as $val) { + $queryParts[] = rawurlencode($key) . '=' . rawurlencode($val); + } + } else { + $queryParts[] = rawurlencode($key) . '=' . rawurlencode($value); + } + } + $queryString = implode('&', $queryParts); + + // Build the string to sign + $stringToSign = sprintf( + '%s/%s/%s?%s', + rawurlencode($workspaceSlug), + rawurlencode($templateSlug), + rawurlencode($inputField), + $queryString + ); + + // Generate signature + $signature = hash_hmac('sha256', $stringToSign, $this->secret); + + // Add signature to query string + $finalQueryString = $queryString . '&sig=' . rawurlencode('sha256:' . $signature); + + // Build final URL + return sprintf( + 'https://%s.tlcdn.com/%s/%s?%s', + rawurlencode($workspaceSlug), + rawurlencode($templateSlug), + rawurlencode($inputField), + $finalQueryString + ); + } } diff --git a/lib/transloadit/TransloaditRequest.php b/lib/transloadit/TransloaditRequest.php index d0a97c3..ec6c7e5 100644 --- a/lib/transloadit/TransloaditRequest.php +++ b/lib/transloadit/TransloaditRequest.php @@ -5,6 +5,7 @@ class TransloaditRequest extends CurlRequest { public $key = null; public $secret = null; + public $protocol = 'https'; public $endpoint = 'https://api2.transloadit.com'; public $path = null; @@ -88,7 +89,12 @@ public function execute($response = null) { } private function _waitForCompletion($response) { - $assemblyUrl = $response->data['assembly_ssl_url']; + // Try assembly_ssl_url first, fall back to assembly_url + $assemblyUrl = $response->data['assembly_ssl_url'] ?? $response->data['assembly_url'] ?? null; + if (!$assemblyUrl) { + throw new \RuntimeException('No assembly URL found in response. Response data: ' . json_encode($response->data)); + } + $parts = parse_url($assemblyUrl); while (true) { diff --git a/lib/transloadit/TransloaditResponse.php b/lib/transloadit/TransloaditResponse.php index 972c791..179c170 100644 --- a/lib/transloadit/TransloaditResponse.php +++ b/lib/transloadit/TransloaditResponse.php @@ -3,6 +3,10 @@ namespace transloadit; class TransloaditResponse extends CurlResponse { + public $method; + public $url; + public $fields; + public function error() { $error = parent::error(); if ($error) { diff --git a/test/bootstrap.php b/test/bootstrap.php index b224a99..b9e024f 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -2,8 +2,6 @@ namespace transloadit\test; -// use PHPUnit\Framework\TestCase; - require dirname(__DIR__) . "/vendor/autoload.php"; if (file_exists(__DIR__ . '/config.php')) { @@ -12,38 +10,38 @@ define('TEST_FIXTURE_DIR', __DIR__ . '/fixture'); - - abstract class SystemTestCase extends \PHPUnit\Framework\TestCase { + protected \transloadit\TransloaditRequest $request; + public function setUp(): void { - if (!defined('TEST_ACCOUNT_KEY') || !defined('TEST_ACCOUNT_SECRET')) { + if (!defined('TRANSLOADIT_KEY') || !defined('TRANSLOADIT_SECRET')) { $this->markTestSkipped( - 'Have a look at test/config.php.template to get this test to run.' + 'Have a look at test/config.php to get this test to run.' ); return; } - // @todo Load config from git excluded config file $this->request = new \transloadit\TransloaditRequest([ - 'key' => TEST_ACCOUNT_KEY, - 'secret' => TEST_ACCOUNT_SECRET, + 'key' => TRANSLOADIT_KEY, + 'secret' => TRANSLOADIT_SECRET, ]); } } class TransloaditRequestTestCase extends \PHPUnit\Framework\TestCase { + protected \transloadit\Transloadit $transloadit; + public function setUp(): void { - if (!defined('TEST_ACCOUNT_KEY') || !defined('TEST_ACCOUNT_SECRET')) { + if (!defined('TRANSLOADIT_KEY') || !defined('TRANSLOADIT_SECRET')) { $this->markTestSkipped( - 'Have a look at test/config.php.template to get this test to run.' + 'Have a look at test/config.php to get this test to run.' ); return; } - // @todo Load config from git excluded config file - $this->transloadit = new transloadit\Transloadit([ - 'key' => TEST_ACCOUNT_KEY, - 'secret' => TEST_ACCOUNT_SECRET, + $this->transloadit = new \transloadit\Transloadit([ + 'key' => TRANSLOADIT_KEY, + 'secret' => TRANSLOADIT_SECRET, ]); } } diff --git a/test/config.php b/test/config.php index ea79ff8..56fdb8d 100644 --- a/test/config.php +++ b/test/config.php @@ -7,13 +7,12 @@ continue; } list($key, $value) = explode('=', $line, 2); - define($key, $value); + + define(str_replace('export ', '', $key), str_replace('"', '', str_replace("'", '', $value))); } } else { - if (getenv('TEST_ACCOUNT_KEY')) { - define('TEST_ACCOUNT_KEY', getenv('TEST_ACCOUNT_KEY')); - } - if (getenv('TEST_ACCOUNT_SECRET')) { - define('TEST_ACCOUNT_SECRET', getenv('TEST_ACCOUNT_SECRET')); + if (getenv('TRANSLOADIT_KEY') && getenv('TRANSLOADIT_SECRET')) { + define('TRANSLOADIT_KEY', getenv('TRANSLOADIT_KEY')); + define('TRANSLOADIT_SECRET', getenv('TRANSLOADIT_SECRET')); } } diff --git a/test/simple/TransloaditTest.php b/test/simple/TransloaditTest.php index 5e4ed1c..12c44e4 100644 --- a/test/simple/TransloaditTest.php +++ b/test/simple/TransloaditTest.php @@ -145,4 +145,244 @@ public function testCreateAssemblyForm() { $this->assertTrue(preg_match('/value="' . $val . '"/', $inputTag) !== false); } } + + private function getExpectedUrl(array $params): ?string { + if (getenv('TEST_NODE_PARITY') !== '1') { + return null; + } + + // Check for tsx before trying to use it + exec('which tsx 2>/dev/null', $output, $returnVar); + if ($returnVar !== 0) { + throw new \RuntimeException('tsx command not found. Please install it with: npm install -g tsx'); + } + + $scriptPath = __DIR__ . '/../../tool/node-smartcdn-sig.ts'; + $jsonInput = json_encode($params); + + $descriptorspec = [ + 0 => ["pipe", "r"], // stdin + 1 => ["pipe", "w"], // stdout + 2 => ["pipe", "w"] // stderr + ]; + + $process = proc_open("tsx $scriptPath", $descriptorspec, $pipes); + + if (!is_resource($process)) { + throw new \RuntimeException('Failed to start Node script'); + } + + fwrite($pipes[0], $jsonInput); + fclose($pipes[0]); + + $output = stream_get_contents($pipes[1]); + $error = stream_get_contents($pipes[2]); + + fclose($pipes[1]); + fclose($pipes[2]); + + $exitCode = proc_close($process); + + if ($exitCode !== 0) { + throw new \RuntimeException("Node script failed: $error"); + } + + return trim($output); + } + + private function assertParityWithNode(string $url, array $params, string $message = ''): void { + $expectedUrl = $this->getExpectedUrl($params); + if ($expectedUrl !== null) { + $this->assertEquals($expectedUrl, $url, $message ?: 'URL should match Node.js reference implementation'); + } + } + + private function debugUrls(string $phpUrl, string $nodeUrl, string $message = ''): void { + echo "\n\nDebug $message:\n"; + echo "PHP URL: $phpUrl\n"; + echo "Node URL: $nodeUrl\n\n"; + } + + public function testSignedSmartCDNUrl() { + $transloadit = new Transloadit([ + 'key' => 'test-key', + 'secret' => 'test-secret' + ]); + + // Use fixed timestamp for all tests + $expireAtMs = 1732550672867; + + // Test basic URL generation + $params = [ + 'workspace' => 'workspace', + 'template' => 'template', + 'input' => 'file.jpg', + 'auth_key' => 'test-key', + 'auth_secret' => 'test-secret', + 'expire_at_ms' => $expireAtMs + ]; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + [], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/file.jpg?auth_key=test-key&exp=1732550672867&sig=sha256%3Ad994b8a737db1c43d6e04a07018dc33e8e28b23b27854bd6383d828a212cfffb'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + + // Test with input field + $params['input'] = 'input.jpg'; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + [], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/input.jpg?auth_key=test-key&exp=1732550672867&sig=sha256%3A75991f02828d194792c9c99f8fea65761bcc4c62dbb287a84f642033128297c0'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + + // Test with additional params + $params['input'] = 'file.jpg'; + $params['url_params'] = ['width' => 100]; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + $params['url_params'], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/file.jpg?auth_key=test-key&exp=1732550672867&width=100&sig=sha256%3Ae5271d8fb6482d9351ebe4285b6fc75539c4d311ff125c4d76d690ad71c258ef'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + + // Test with empty param string + $params['url_params'] = ['width' => '', 'height' => '200']; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + $params['url_params'], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/file.jpg?auth_key=test-key&exp=1732550672867&height=200&width=&sig=sha256%3A1a26733c859f070bc3d83eb3174650d7a0155642e44a5ac448a43bc728bc0f85'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + + // Test with null width parameter (should be excluded) + $params['url_params'] = ['width' => null, 'height' => '200']; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + $params['url_params'], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/file.jpg?auth_key=test-key&exp=1732550672867&height=200&sig=sha256%3Adb740ebdfad6e766ebf6516ed5ff6543174709f8916a254f8d069c1701cef517'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + + // Test with only empty width parameter + $params['url_params'] = ['width' => '']; + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + $params['url_params'], + $expireAtMs + ); + $nodeUrl = $this->getExpectedUrl($params); + $expectedUrl = 'https://workspace.tlcdn.com/template/file.jpg?auth_key=test-key&exp=1732550672867&width=&sig=sha256%3A840426f9ac72dde02fd080f09b2304d659fdd41e630b1036927ec1336c312e9d'; + $this->assertEquals($expectedUrl, $url, 'PHP URL should match expected'); + $this->assertEquals($expectedUrl, $nodeUrl, 'Node.js URL should match expected'); + } + + public function testTsxRequiredForParityTesting(): void { + if (getenv('TEST_NODE_PARITY') !== '1') { + $this->markTestSkipped('Parity testing not enabled'); + } + + // Temporarily override PATH to simulate missing tsx + $originalPath = getenv('PATH'); + putenv('PATH=/usr/bin:/bin'); + + try { + $params = [ + 'workspace' => 'test', + 'template' => 'test', + 'input' => 'test.jpg', + 'auth_key' => 'test', + 'auth_secret' => 'test' + ]; + $this->getExpectedUrl($params); + $this->fail('Expected RuntimeException when tsx is not available'); + } catch (\RuntimeException $e) { + $this->assertStringContainsString('tsx command not found', $e->getMessage()); + $this->assertStringContainsString('npm install -g tsx', $e->getMessage()); + } finally { + // Restore original PATH + putenv("PATH=$originalPath"); + } + } + + public function testExpireAtMs(): void { + $transloadit = new Transloadit([ + 'key' => 'test-key', + 'secret' => 'test-secret' + ]); + + // Test with explicit expireAtMs + $expireAtMs = 1732550672867; + $params = [ + 'workspace' => 'workspace', + 'template' => 'template', + 'input' => 'file.jpg', + 'auth_key' => 'test-key', + 'auth_secret' => 'test-secret', + 'expire_at_ms' => $expireAtMs + ]; + + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'], + [], + $params['expire_at_ms'] + ); + + $this->assertStringContainsString("exp=$expireAtMs", $url); + $this->assertParityWithNode($url, $params); + + // Test default expiry (should be about 1 hour from now) + unset($params['expire_at_ms']); + $url = $transloadit->signedSmartCDNUrl( + $params['workspace'], + $params['template'], + $params['input'] + ); + + $matches = []; + preg_match('/exp=(\d+)/', $url, $matches); + $this->assertNotEmpty($matches[1], 'URL should contain expiry timestamp'); + + $expiry = (int)$matches[1]; + $now = time() * 1000; + $oneHour = 60 * 60 * 1000; + + $this->assertGreaterThan($now, $expiry, 'Expiry should be in the future'); + $this->assertLessThan($now + $oneHour + 5000, $expiry, 'Expiry should be about 1 hour from now'); + $this->assertGreaterThan($now + $oneHour - 5000, $expiry, 'Expiry should be about 1 hour from now'); + + // For parity test, set the exact expiry time to match Node.js + $params['expire_at_ms'] = $expiry; + $this->assertParityWithNode($url, $params); + } } diff --git a/test/system/Transloadit/TransloaditAssemblyCreateTest.php b/test/system/Transloadit/TransloaditAssemblyCreateTest.php index 5a9d7e1..f89bd48 100644 --- a/test/system/Transloadit/TransloaditAssemblyCreateTest.php +++ b/test/system/Transloadit/TransloaditAssemblyCreateTest.php @@ -5,19 +5,22 @@ use transloadit\Transloadit; class TransloaditAssemblyCreateTest extends \PHPUnit\Framework\TestCase { + private Transloadit $transloadit; + public function setUp(): void { - if (!defined('TEST_ACCOUNT_KEY') || !defined('TEST_ACCOUNT_SECRET')) { + if (!getenv('TRANSLOADIT_KEY') || !getenv('TRANSLOADIT_SECRET')) { $this->markTestSkipped( - 'Have a look at test/config.php.template to get this test to run.' + 'TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables are required.' ); return; } - // @todo Load config from git excluded config file - $this->transloadit = new Transloadit(['key' => TEST_ACCOUNT_KEY, - 'secret' => TEST_ACCOUNT_SECRET, + $this->transloadit = new Transloadit([ + 'key' => getenv('TRANSLOADIT_KEY'), + 'secret' => getenv('TRANSLOADIT_SECRET'), ]); } + public function testRoot() { $response = $this->transloadit->createAssembly([ 'files' => [TEST_FIXTURE_DIR . '/image-resize-robot.jpg'], diff --git a/test/system/Transloadit/TransloaditCreateAssemblyWaitForCompletionTest.php b/test/system/Transloadit/TransloaditCreateAssemblyWaitForCompletionTest.php index 47fabf8..49b209f 100644 --- a/test/system/Transloadit/TransloaditCreateAssemblyWaitForCompletionTest.php +++ b/test/system/Transloadit/TransloaditCreateAssemblyWaitForCompletionTest.php @@ -8,17 +8,17 @@ class TransloaditCreateAssemblyWaitForCompletionTest extends \PHPUnit\Framework\ private Transloadit $transloadit; public function setUp(): void { - if (!defined('TEST_ACCOUNT_KEY') || !defined('TEST_ACCOUNT_SECRET')) { + if (!defined('TRANSLOADIT_KEY') || !defined('TRANSLOADIT_SECRET')) { $this->markTestSkipped( - 'Have a look at test/config.php.template to get this test to run.' + 'Have a look at test/config.php to get this test to run.' ); return; } // @todo Load config from git excluded config file $this->transloadit = new Transloadit([ - 'key' => TEST_ACCOUNT_KEY, - 'secret' => TEST_ACCOUNT_SECRET, + 'key' => TRANSLOADIT_KEY, + 'secret' => TRANSLOADIT_SECRET, ]); } public function testRoot() { diff --git a/tool/node-smartcdn-sig.ts b/tool/node-smartcdn-sig.ts new file mode 100755 index 0000000..2873f84 --- /dev/null +++ b/tool/node-smartcdn-sig.ts @@ -0,0 +1,91 @@ +#!/usr/bin/env tsx +// Reference Smart CDN (https://transloadit.com/services/content-delivery/) Signature implementation +// And CLI tester to see if our SDK's implementation +// matches Node's + +/// + +import { createHash, createHmac } from 'crypto' + +interface SmartCDNParams { + workspace: string + template: string + input: string + expire_at_ms?: number + auth_key?: string + auth_secret?: string + url_params?: Record +} + +function signSmartCDNUrl(params: SmartCDNParams): string { + const { + workspace, + template, + input, + expire_at_ms, + auth_key, + auth_secret, + url_params = {}, + } = params + + if (!workspace) throw new Error('workspace is required') + if (!template) throw new Error('template is required') + if (input === null || input === undefined) + throw new Error('input must be a string') + if (!auth_key) throw new Error('auth_key is required') + if (!auth_secret) throw new Error('auth_secret is required') + + const workspaceSlug = encodeURIComponent(workspace) + const templateSlug = encodeURIComponent(template) + const inputField = encodeURIComponent(input) + + const expireAt = expire_at_ms ?? Date.now() + 60 * 60 * 1000 // 1 hour default + + const queryParams: Record = {} + + // Handle url_params + Object.entries(url_params).forEach(([key, value]) => { + if (value === null || value === undefined) return + if (Array.isArray(value)) { + value.forEach((val) => { + if (val === null || val === undefined) return + ;(queryParams[key] ||= []).push(String(val)) + }) + } else { + queryParams[key] = [String(value)] + } + }) + + queryParams.auth_key = [auth_key] + queryParams.exp = [String(expireAt)] + + // Sort parameters to ensure consistent ordering + const sortedParams = Object.entries(queryParams) + .sort() + .map(([key, values]) => + values.map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`) + ) + .flat() + .join('&') + + const stringToSign = `${workspaceSlug}/${templateSlug}/${inputField}?${sortedParams}` + const signature = createHmac('sha256', auth_secret) + .update(stringToSign) + .digest('hex') + + const finalParams = `${sortedParams}&sig=${encodeURIComponent( + `sha256:${signature}` + )}` + return `https://${workspaceSlug}.tlcdn.com/${templateSlug}/${inputField}?${finalParams}` +} + +// Read JSON from stdin +let jsonInput = '' +process.stdin.on('data', (chunk) => { + jsonInput += chunk +}) + +process.stdin.on('end', () => { + const params = JSON.parse(jsonInput) + console.log(signSmartCDNUrl(params)) +})