diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..0758a88
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,9 @@
+DB_DRIVER=mysql
+DB_HOST=mysql
+DB_PORT=3306
+
+CI_CLOUD_TASKS_PROJECT_ID=
+CI_CLOUD_TASKS_QUEUE=
+CI_CLOUD_TASKS_LOCATION=
+CI_CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL=
+CI_SERVICE_ACCOUNT_JSON_KEY_PATH=./tests/Support/gcloud-key-valid.json
diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md
new file mode 100644
index 0000000..ba1c3d9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/issue-template.md
@@ -0,0 +1,14 @@
+---
+name: Issue template
+about: Help you create an effective issue and know what to expect.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+Thank you for taking the time for reporting a bug, requesting a feature, or letting me know something else about the package.
+
+I create open-source packages for fun while working full-time and running my own business. That means I don't have as much time left to maintain these packages, build elaborate new features or investigate and fix bugs. If you wish to get a feature or bugfix merged it would be greatly appreciated if you can provide as much info as possible and preferably a Pull Request ready with automated tests. Realistically I check Github a few times a week, and take several days, weeks or sometimes months before finishing features/bugfixes (depending on their size of course).
+
+Thanks for understanding. 😁
diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml
new file mode 100644
index 0000000..b6819a6
--- /dev/null
+++ b/.github/workflows/code-analysis.yml
@@ -0,0 +1,35 @@
+name: Code analysis
+
+on:
+ push:
+ paths:
+ - '**.php'
+ - 'phpstan.neon'
+
+permissions:
+ contents: write
+
+jobs:
+ php-code-styling:
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.head_ref }}
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ coverage: none
+
+ - name: Install dependencies
+ run: |
+ composer install --no-interaction --prefer-dist
+
+ - name: Run code analysis
+ run: |
+ composer run larastan
\ No newline at end of file
diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml
new file mode 100644
index 0000000..6cea71b
--- /dev/null
+++ b/.github/workflows/code-style.yml
@@ -0,0 +1,28 @@
+name: Code style
+
+on:
+ push:
+ paths:
+ - '**.php'
+
+permissions:
+ contents: write
+
+jobs:
+ php-code-styling:
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.head_ref }}
+
+ - name: Check code style
+ uses: aglipanci/laravel-pint-action@v2
+
+ - name: Commit changes
+ uses: stefanzweifel/git-auto-commit-action@v5
+ with:
+ commit_message: Apply code style rules
\ No newline at end of file
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 07e4ae4..4e085ea 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -1,54 +1,130 @@
name: Run tests
on:
- push:
+ pull_request_target:
+ types: [ opened, synchronize, labeled ]
schedule:
- cron: '0 0 * * *'
jobs:
- php-tests:
- runs-on: ${{ matrix.os }}
+ access_check:
+ runs-on: ubuntu-latest
+ name: Access check
+ steps:
+ - name: Ensure pull-request is safe to run
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ if (context.eventName === 'schedule') {
+ return
+ }
+
+ // If the user that pushed the commit is a maintainer, skip the check
+ const collaborators = await github.rest.repos.listCollaborators({
+ owner: context.repo.owner,
+ repo: context.repo.repo
+ });
+
+ if (collaborators.data.some(c => c.login === context.actor)) {
+ console.log(`User ${context.actor} is allowed to run tests because they are a collaborator.`);
+ return
+ }
+
+ const issue_number = context.issue.number;
+ const repository = context.repo.repo;
+ const owner = context.repo.owner;
+
+ const response = await github.rest.issues.listLabelsOnIssue({
+ owner,
+ repo: repository,
+ issue_number
+ });
+ const labels = response.data.map(label => label.name);
+ let hasLabel = labels.includes('safe-to-test')
+
+ if (context.payload.action === 'synchronize' && hasLabel) {
+ hasLabel = false
+ await github.rest.issues.removeLabel({
+ owner,
+ repo: repository,
+ issue_number,
+ name: 'safe-to-test'
+ });
+ }
+
+ if (!hasLabel) {
+ throw "Action was not authorized. Exiting now."
+ }
+ php-tests:
+ runs-on: ubuntu-latest
+ needs: access_check
strategy:
matrix:
- php: [7.4, 7.3, 7.2]
- laravel: [7.*, 6.*, 5.8.*, 5.7.*, 5.6.*]
- os: [ubuntu-latest]
- include:
- - laravel: 7.*
- testbench: 5.*
- - laravel: 6.*
- testbench: 4.*
- - laravel: 5.8.*
- testbench: 3.8.*
- - laravel: 5.7.*
- testbench: 3.7.*
- - laravel: 5.6.*
- testbench: 3.6.*
- exclude:
- - laravel: 5.7.*
- php: 7.4
- - laravel: 5.6.*
- php: 7.4
- - laravel: 5.5.*
- php: 7.4
+ db:
+ - { driver: 'mysql', version: '8.0' }
+ - { driver: 'mysql', version: '8.4' }
+ - { driver: 'pgsql', version: '14' }
+ - { driver: 'pgsql', version: '15' }
+ - { driver: 'pgsql', version: '16' }
+ - { driver: 'pgsql', version: '17' }
+ payload:
+ - { queue: 'github-actions-laravel11-php82', laravel: '11.*', php: '8.2', 'testbench': '9.*' }
+ - { queue: 'github-actions-laravel11-php83', laravel: '11.*', php: '8.3', 'testbench': '9.*' }
+ - { queue: 'github-actions-laravel11-php84', laravel: '11.*', php: '8.4', 'testbench': '9.*' }
+ - { queue: 'github-actions-laravel12-php82', laravel: '12.*', php: '8.2', 'testbench': '10.*' }
+ - { queue: 'github-actions-laravel12-php83', laravel: '12.*', php: '8.3', 'testbench': '10.*' }
+ - { queue: 'github-actions-laravel12-php84', laravel: '12.*', php: '8.4', 'testbench': '10.*' }
- name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
+ name: PHP ${{ matrix.payload.php }} - Laravel ${{ matrix.payload.laravel }} - DB ${{ matrix.db.driver }} ${{ matrix.db.version }}
steps:
- name: Checkout code
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup PHP
- uses: shivammathur/setup-php@v1
+ uses: shivammathur/setup-php@v2
with:
- php-version: ${{ matrix.php }}
+ php-version: ${{ matrix.payload.php }}
extensions: mbstring, dom, fileinfo
coverage: none
+ - name: Set up MySQL and PostgreSQL
+ env:
+ CI_SERVICE_ACCOUNT_JSON_KEY: ${{ secrets.CI_SERVICE_ACCOUNT_JSON_KEY }}
+ run: |
+ touch .env
+ if [ "${{ matrix.db.driver }}" = "mysql" ]; then
+ MYSQL_PORT=3307 MYSQL_VERSION=${{ matrix.db.version }} docker compose up ${{ matrix.db.driver }} -d
+ elif [ "${{ matrix.db.driver }}" = "pgsql" ]; then
+ POSTGRES_PORT=5432 PGSQL_VERSION=${{ matrix.db.version }} docker compose up ${{ matrix.db.driver }} -d
+ fi
- name: Install dependencies
run: |
- composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
- composer update --prefer-stable --prefer-dist --no-interaction --no-suggest
+ composer require "laravel/framework:${{ matrix.payload.laravel }}" "orchestra/testbench:${{ matrix.payload.testbench }}" --no-interaction --no-update
+ composer update --prefer-stable --prefer-dist --no-interaction
+ if [ "${{ matrix.db.driver }}" = "mysql" ]; then
+ while ! mysqladmin ping --host=127.0.0.1 --user=cloudtasks --port=3307 --password=cloudtasks --silent; do
+ echo "Waiting for MySQL..."
+ sleep 1
+ done
+ else
+ echo "Not waiting for MySQL."
+ fi
- name: Execute tests
- run: vendor/bin/phpunit
+ env:
+ DB_DRIVER: ${{ matrix.db.driver }}
+ DB_HOST: 127.0.0.1
+ CI_CLOUD_TASKS_PROJECT_ID: ${{ secrets.CI_CLOUD_TASKS_PROJECT_ID }}
+ CI_CLOUD_TASKS_QUEUE: ${{ secrets.CI_CLOUD_TASKS_QUEUE }}
+ CI_CLOUD_TASKS_LOCATION: ${{ secrets.CI_CLOUD_TASKS_LOCATION }}
+ CI_CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL: ${{ secrets.CI_CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL }}
+ CI_SERVICE_ACCOUNT_JSON_KEY: ${{ secrets.CI_SERVICE_ACCOUNT_JSON_KEY }}
+ CI_CLOUD_TASKS_CUSTOM_QUEUE: ${{ matrix.payload.queue }}
+ run: |
+ echo $CI_SERVICE_ACCOUNT_JSON_KEY > tests/Support/gcloud-key-valid.json
+ touch .env
+ vendor/bin/phpunit
diff --git a/.gitignore b/.gitignore
index 2234772..7c4f5ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
/vendor/
-composer.lock
.idea/
+.phpunit.result.cache
+.phpunit.cache
+.env
+/coverage
\ No newline at end of file
diff --git a/README.md b/README.md
index afacacf..058c27f 100644
--- a/README.md
+++ b/README.md
@@ -1,154 +1,237 @@
-
-
-
-
-
+# Cloud Tasks queue driver for Laravel
+
+[](https://github.com/stackkit/laravel-google-cloud-tasks-queue/actions/workflows/run-tests.yml)
-
-
+
-# Introduction
+This package allows Google Cloud Tasks to be used as the queue driver.
-This package allows you to use Google Cloud Tasks as your queue driver.
+Companion packages: Cloud Scheduler, Cloud Logging
-# How it works
+
-Using Cloud Tasks as a Laravel queue driver is fundamentally different than other Laravel queue drivers, like Redis.
-Typically a Laravel queue has a worker that listens to incoming jobs using the `queue:work` / `queue:listen` command.
-With Cloud Tasks, this is not the case. Instead, Cloud Tasks will schedule the job for you and make an HTTP request to your application with the job payload. There is no need to run a `queue:work/listen` command.
-For more information on how to configure the Cloud Tasks queue, read the next section [Configuring the queue](#configuring-the-queue)
+### Requirements
-This package uses the HTTP request handler and doesn't support AppEngine. But feel free to contribute!
+This package requires Laravel 11 or 12.
-# Requirements
+### Installation
-This package requires Laravel 5.6 or higher.
+Require the package using Composer
-Please check the table below for supported Laravel and PHP versions:
+```shell
+composer require stackkit/laravel-google-cloud-tasks-queue
+```
-|Laravel Version| PHP Version |
-|---|---|
-| 5.6 | 7.2 or 7.3
-| 5.7 | 7.2 or 7.3
-| 5.8 | 7.2 or 7.3 or 7.4
-| 6.x | 7.2 or 7.3 or 7.4
-| 7.x | 7.2 or 7.3 or 7.4
+Add a new queue connection to `config/queue.php`
-# Installation
+```php
+'cloudtasks' => [
+ 'driver' => 'cloudtasks',
+ 'project' => env('CLOUD_TASKS_PROJECT', ''),
+ 'location' => env('CLOUD_TASKS_LOCATION', ''),
+ 'queue' => env('CLOUD_TASKS_QUEUE', 'default'),
+
+ // Required when using AppEngine
+ 'app_engine' => env('APP_ENGINE_TASK', false),
+ 'app_engine_service' => env('APP_ENGINE_SERVICE', ''),
+
+ // Required when not using AppEngine
+ 'handler' => env('CLOUD_TASKS_HANDLER', ''),
+ 'service_account_email' => env('CLOUD_TASKS_SERVICE_EMAIL', ''),
+
+ 'backoff' => 0,
+ 'after_commit' => false,
+ // enable this if you want to set a non-default Google Cloud Tasks dispatch timeout
+ //'dispatch_deadline' => 1800, // in seconds
+],
+```
-(1) Require the package using Composer
+Finally, set the correct environment variables.
-```bash
-composer require stackkit/laravel-google-cloud-tasks-queue
+```dotenv
+QUEUE_CONNECTION=cloudtasks
```
+If you're using Cloud Run:
-[Official documentation - Creating Cloud Tasks queues](https://cloud.google.com/tasks/docs/creating-queues)
+```dotenv
+CLOUD_TASKS_PROJECT=my-project
+CLOUD_TASKS_LOCATION=europe-west6
+CLOUD_TASKS_QUEUE=barbequeue
+CLOUD_TASKS_SERVICE_EMAIL=my-service-account@appspot.gserviceaccount.com
+# Optionally (when using a separate task handler):
+CLOUD_TASKS_SERVICE_HANDLER=
+```
-(2) Add a new queue connection to `config/queue.php`
+If you're using App Engine:
+```dotenv
+CLOUD_TASKS_PROJECT=my-project
+CLOUD_TASKS_LOCATION=europe-west6
+CLOUD_TASKS_QUEUE=barbequeue
+APP_ENGINE_TASK=true
+APP_ENGINE_SERVICE=my-service
```
-'cloudtasks' => [
- 'driver' => 'cloudtasks',
- 'credentials' => base_path('gcloud-key.json'),
- 'project' => env('STACKKIT_CLOUD_TASKS_PROJECT', ''),
- 'location' => env('STACKKIT_CLOUD_TASKS_LOCATION', ''),
- 'handler' => env('STACKKIT_CLOUD_TASKS_HANDLER', ''),
- 'queue' => env('STACKKIT_CLOUD_TASKS_QUEUE', 'default'),
- 'service_account_email' => env('STACKKIT_CLOUD_TASKS_SERVICE_EMAIL', ''),
-],
+
+Please check the table below on what the values mean and what their value should be.
+
+| Environment variable | Description | Example
+---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------
+| `CLOUD_TASKS_PROJECT` | The project your queue belongs to. | `my-project`
+| `CLOUD_TASKS_LOCATION` | The region where the project is hosted. | `europe-west6`
+| `CLOUD_TASKS_QUEUE` | The default queue a job will be added to. | `emails`
+| **App Engine**
+| `APP_ENGINE_TASK` (optional) | Set to true to use App Engine task (else a Http task will be used). Defaults to false. | `true`
+| `APP_ENGINE_SERVICE` (optional) | The App Engine service to handle the task (only if using App Engine task). | `api`
+| **Non- App Engine apps**
+| `CLOUD_TASKS_SERVICE_EMAIL` (optional) | The email address of the service account. Important, it should have the correct roles. See the section below which roles. | `my-service-account@appspot.gserviceaccount.com`
+| `CLOUD_TASKS_HANDLER` (optional) | The URL that Cloud Tasks will call to process a job. This should be the URL to your Laravel app. By default we will use the URL that dispatched the job. | `https://.com`
+
+
+
+Optionally, you may publish the config file:
+
+```console
+php artisan vendor:publish --tag=cloud-tasks
```
-(3) Update the `QUEUE_CONNECTION` environment variable
+If you are using separate services for dispatching and handling tasks, and your application only dispatches jobs and should not be able to handle jobs, you may disable the task handler from `config/cloud-tasks.php`:
+```php
+'disable_task_handler' => env('CLOUD_TASKS_DISABLE_TASK_HANDLER', false),
```
-QUEUE_CONNECTION=cloudtasks
+
+### How to
+
+#### Passing headers to a task
+
+You can pass headers to a task by using the `setTaskHeadersUsing` method on the `CloudTasksQueue` class.
+
+```php
+use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksQueue;
+
+CloudTasksQueue::setTaskHeadersUsing(static fn() => [
+ 'X-My-Header' => 'My-Value',
+]);
```
-(4) Create a new Cloud Tasks queue using `gcloud`
+If necessary, the current payload being dispatched is also available:
-````bash
-gcloud tasks queues create [QUEUE_ID]
-````
+```php
+use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksQueue;
-Now that the package is installed, the final step is to set the correct environment variables.
+CloudTasksQueue::setTaskHeadersUsing(static fn(array $payload) => [
+ 'X-My-Header' => $payload['displayName'],
+]);
+```
-Please check the table below on what the values mean and what their value should be.
+#### Configure task handler url
-|Environment variable|Description|Example
-|---|---|---
-|`STACKKIT_CLOUD_TASKS_PROJECT`|The project your queue belongs to.|`my-project`
-|`STACKKIT_CLOUD_TASKS_LOCATION`|The region where the AppEngine is hosted|`europe-west6`
-|`STACKKIT_CLOUD_TASKS_HANDLER`|The URL that Cloud Tasks will call to process a job. This should be the URL to your Laravel app with the `handle-task` path added|`https://.com/handle-task`
-|`STACKKIT_CLOUD_TASKS_QUEUE`|The queue a job will be added to|`emails`
-|`STACKKIT_CLOUD_TASKS_SERVICE_EMAIL`|The email address of the AppEngine service account. Important, it should have the *Cloud Tasks Enqueuer* role. This is used for securing the handler.|`my-service-account@appspot.gserviceaccount.com`
+You can set the handler url for a task by using the `configureHandlerUrlUsing` method on the `CloudTasksQueue` class.
+
+```php
+use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksQueue;
+
+CloudTasksQueue::configureHandlerUrlUsing(static fn() => 'https://example.com/my-url');
+```
-# Configuring the queue
+If necessary, the current job being dispatched is also available:
-When you first create a queue using `gcloud tasks queues create`, the default settings will look something like this:
+```php
+use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksQueue;
+CloudTasksQueue::configureHandlerUrlUsing(static fn(MyJob $job) => 'https://example.com/my-url/' . $job->something());
```
-rateLimits:
- maxBurstSize: 100
- maxConcurrentDispatches: 1000
- maxDispatchesPerSecond: 500.0
-retryConfig:
- maxAttempts: 100
- maxBackoff: 3600s
- maxDoublings: 16
- minBackoff: 0.100s
+
+#### Configure worker options
+
+You can configure worker options by using the `configureWorkerOptionsUsing` method on the `CloudTasksQueue` class.
+
+```php
+use Stackkit\LaravelGoogleCloudTasksQueue\IncomingTask;
+
+CloudTasksQueue::configureWorkerOptionsUsing(function (IncomingTask $task) {
+ $queueTries = [
+ 'high' => 5,
+ 'low' => 1,
+ ];
+
+ return new WorkerOptions(maxTries: $queueTries[$task->queue()] ?? 1);
+});
```
-## Configurable settings
+#### Use a custom credentials file
-### maxBurstSize
+Modify (or add) the `client_options` key in the `config/cloud-tasks.php` file:
-Max burst size limits how fast tasks in queue are processed when many tasks are in the queue and the rate is high.
+```php
+'client_options' => [
+ 'credentials' => '/path/to/credentials.json',
+]
+```
-### maxConcurrentDispatches
-The maximum number of concurrent tasks that Cloud Tasks allows to be dispatched for this queue
+#### Modify CloudTasksClient options
-### maxDispatchesPerSecond
+Modify (or add) the `client_options` key in the `config/cloud-tasks.php` file:
-The maximum rate at which tasks are dispatched from this queue.
+```php
+'client_options' => [
+ // custom options here
+]
+```
+
+### How it works and differences
-### maxAttempts
+Using Cloud Tasks as a Laravel queue driver is fundamentally different than other Laravel queue drivers, like Redis.
-Number of attempts per task. Cloud Tasks will attempt the task max_attempts times (that is, if the first attempt fails, then there will be max_attempts - 1 retries). Must be >= -1.|
+Typically a Laravel queue has a worker that listens to incoming jobs using the `queue:work` / `queue:listen` command.
+With Cloud Tasks, this is not the case. Instead, Cloud Tasks will schedule the job for you and make an HTTP request to
+your application with the job payload. There is no need to run a `queue:work/listen` command.
-### maxBackoff
+#### Good to know
-A task will be scheduled for retry between min_backoff and max_backoff duration after it fails
+Cloud Tasks has it's own retry configuration options: maximum number of attempts, retry duration, min/max backoff and max doublings. All of these options are ignored by this package. Instead, you may configure max attempts, retry duration and backoff strategy right from Laravel.
-### maxDoublings
+### Authentication
-The time between retries will double max_doublings times.
+If you're not using your master service account (which has all abilities), you must add the following roles to make it
+works:
-A task's retry interval starts at min_backoff, then doubles max_doublings times, then increases linearly, and finally retries retries at intervals of max_backoff up to max_attempts times.
-
-For example, if min_backoff is 10s, max_backoff is 300s, and max_doublings is 3, then the a task will first be retried in 10s. The retry interval will double three times, and then increase linearly by 2^3 * 10s. Finally, the task will retry at intervals of max_backoff until the task has been attempted max_attempts times. Thus, the requests will retry at 10s, 20s, 40s, 80s, 160s, 240s, 300s, 300s, ....
+1. App Engine Viewer
+2. Cloud Tasks Enqueuer
+3. Cloud Tasks Viewer
+4. Cloud Tasks Task Deleter
+5. Service Account User
-## Recommended settings for Laravel
+### Upgrading
-To simulate a single `queue:work/queue:listen` process, simply set the `maxConcurrentDispatches` to 1:
+Read [UPGRADING.MD](UPGRADING.md) on how to update versions.
-```
-gcloud tasks queues update [QUEUE_ID] --max-concurrent-dispatches=1
-```
+### Troubleshooting
-More information on configuring queues:
+#### HttpRequest.url must start with 'https://'
-https://cloud.google.com/tasks/docs/configuring-queues
+This can happen when your application runs behind a reverse proxy. To fix this, add the application domain to Laravel's [trusted proxies](https://laravel.com/docs/11.x/requests#trusting-all-proxies). You may need to add the wildcard `*` as trusted proxy.
+
+#### Maximum call stack size (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
+
+This currently seems to be a bug with PHP 8.3 and `googleapis/gax-php`. See [this issue](https://github.com/googleapis/gax-php/issues/584) for more information.
+
+A potential workaround is to disable PHP 8.3 call stack limit by setting this value in `php.ini`:
+
+```ini
+zend.max_allowed_stack_size: -1
+```
-# Security
+### Contributing
-The job handler requires each request to have an OpenID token. In the installation step we set the service account email, and with that service account, Cloud Tasks will generate an OpenID token and send it along with the job payload to the handler.
+You can use the services defined in `docker-compose.yml` to start running the package.
-This package verifies that the token is digitally signed by Google. Only Google Tasks will be able to call your handler.
+Inside the container, run `composer install`.
-More information about OpenID Connect:
+Set up the environment: `cp .env.example .env`
-https://developers.google.com/identity/protocols/oauth2/openid-connect
\ No newline at end of file
+Some tests hit the Cloud Tasks API and need a project and key to be able to hit it. See the variables in `.env`
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..96eed0f
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,68 @@
+# From 4.x to 5.x
+
+The package drops support for Laravel 10 and adds support for Laravel 12.
+
+## Configuration type strictness (Impact: low)
+
+The package now uses `config()->string()` and `config()->array()` to enforce receiving the correct types from the Laravel configuration file.
+
+This should not give any problems but you should verify the configuration settings.
+
+There are no other breaking changes.
+
+# From 3.x to 4.x
+
+## Renamed environment names (Impact: high)
+
+The following environment variables have been shortened:
+- `STACKKIT_CLOUD_TASKS_PROJECT` → `CLOUD_TASKS_PROJECT`
+- `STACKKIT_CLOUD_TASKS_LOCATION` → `CLOUD_TASKS_LOCATION`
+- `STACKKIT_CLOUD_TASKS_QUEUE` → `CLOUD_TASKS_QUEUE`
+- `STACKKIT_CLOUD_TASKS_HANDLER` → `CLOUD_TASKS_HANDLER`
+- `STACKKIT_CLOUD_TASKS_SERVICE_EMAIL` → `CLOUD_TASKS_SERVICE_EMAIL`
+
+The following environment variables have been renamed to be more consistent:
+
+- `STACKKIT_APP_ENGINE_TASK` → `CLOUD_TASKS_APP_ENGINE_TASK`
+- `STACKKIT_APP_ENGINE_SERVICE` → `CLOUD_TASKS_APP_ENGINE_SERVICE`
+
+The following environment variable has been removed:
+- `STACKKIT_CLOUD_TASKS_SIGNED_AUDIENCE`
+
+## Removed dashboard (Impact: high)
+
+The dashboard has been removed to keep the package minimal. A separate composer package might be created with an updated version of the dashboard.
+
+## New configuration file (Impact: medium)
+
+The configuration file has been updated to reflect the removed dashboard and to add new configurable options.
+
+Please publish the new configuration file:
+
+```shell
+php artisan vendor:publish --tag=cloud-tasks --force
+```
+
+## Dispatch deadline (Impact: medium)
+
+The `dispatch_deadline` has been removed from the task configuration. You may now use Laravel's timeout configuration to control the maximum execution time of a task.
+
+
+# From 2.x to 3.x
+
+PHP 7.2 and 7.3, and Laravel 5.x are no longer supported.
+
+## Update handler URL (Impact: high)
+
+The handler URL environment has been simplified. Please change it like this:
+
+```dotenv
+# Before
+STACKKIT_CLOUD_TASKS_HANDLER=https://my-app/handle-task
+# After
+STACKKIT_CLOUD_TASKS_HANDLER=https://my-app
+```
+
+It's also allowed to remove this variable entirely in 3.x: The package will automatically use the application URL if the `STACKKIT_CLOUD_TASKS_HANDLER`
+environment is not present. If you omit it, please ensure the [trusted proxy](https://laravel.com/docs/9.x/requests#configuring-trusted-proxies) have been configured
+in your application. Otherwise, you might run into weird issues. :-)
diff --git a/app.Dockerfile b/app.Dockerfile
new file mode 100644
index 0000000..f43bf6f
--- /dev/null
+++ b/app.Dockerfile
@@ -0,0 +1,6 @@
+FROM serversideup/php:8.4-fpm
+
+USER root
+RUN install-php-extensions bcmath
+
+USER www-data
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 3d37742..179f1ec 100644
--- a/composer.json
+++ b/composer.json
@@ -1,5 +1,7 @@
{
"name": "stackkit/laravel-google-cloud-tasks-queue",
+ "description": "Google Cloud Tasks queue driver for Laravel",
+ "keywords": ["laravel", "queue", "queues", "google", "cloudtasks", "cloud", "run"],
"license": "MIT",
"authors": [
{
@@ -8,15 +10,16 @@
}
],
"require": {
+ "php": "^8.1",
"ext-json": "*",
- "google/cloud-tasks": "^1.6",
- "firebase/php-jwt": "^5.2",
- "phpseclib/phpseclib": "~2.0"
+ "google/cloud-tasks": "^2.0",
+ "thecodingmachine/safe": "^3.0"
},
"require-dev": {
- "mockery/mockery": "^1.2",
- "orchestra/testbench": "^3.5 || ^3.6 || ^3.7 || ^3.8 || ^4.0 || ^5.0",
- "symfony/console": "^4.4|^5.0"
+ "orchestra/testbench": "^10.0",
+ "thecodingmachine/phpstan-safe-rule": "^1.2",
+ "laravel/pint": "^1.13",
+ "larastan/larastan": "^3.0"
},
"autoload": {
"psr-4": {
@@ -34,5 +37,23 @@
"Stackkit\\LaravelGoogleCloudTasksQueue\\CloudTasksServiceProvider"
]
}
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "scripts": {
+ "l11": [
+ "composer require laravel/framework:11.* orchestra/testbench:9.* --no-interaction --no-update",
+ "composer update --prefer-stable --prefer-dist --no-interaction"
+ ],
+ "l12": [
+ "composer require laravel/framework:12.* orchestra/testbench:10.* --no-interaction --no-update",
+ "composer update --prefer-stable --prefer-dist --no-interaction"
+ ],
+ "pint": [
+ "pint"
+ ],
+ "larastan": [
+ "@php -d memory_limit=-1 vendor/bin/phpstan"
+ ]
}
}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..6df29a5
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,9601 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "cabd4a7b08023ecd8d02afc68d020650",
+ "packages": [
+ {
+ "name": "brick/math",
+ "version": "0.12.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/brick/math.git",
+ "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40",
+ "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^10.1",
+ "vimeo/psalm": "6.8.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Brick\\Math\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Arbitrary-precision arithmetic library",
+ "keywords": [
+ "Arbitrary-precision",
+ "BigInteger",
+ "BigRational",
+ "arithmetic",
+ "bigdecimal",
+ "bignum",
+ "bignumber",
+ "brick",
+ "decimal",
+ "integer",
+ "math",
+ "mathematics",
+ "rational"
+ ],
+ "support": {
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.12.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/BenMorel",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-26T10:21:45+00:00"
+ },
+ {
+ "name": "firebase/php-jwt",
+ "version": "v6.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/8f718f4dfc9c5d5f0c994cdfd103921b43592712",
+ "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "^7.4",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.5",
+ "psr/cache": "^2.0||^3.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0"
+ },
+ "suggest": {
+ "ext-sodium": "Support EdDSA (Ed25519) signatures",
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/firebase/php-jwt/issues",
+ "source": "https://github.com/firebase/php-jwt/tree/v6.11.0"
+ },
+ "time": "2025-01-23T05:11:06+00:00"
+ },
+ {
+ "name": "google/auth",
+ "version": "v1.46.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-auth-library-php.git",
+ "reference": "7fafae99a41984cbfb92508174263cf7bf3049b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/7fafae99a41984cbfb92508174263cf7bf3049b9",
+ "reference": "7fafae99a41984cbfb92508174263cf7bf3049b9",
+ "shasum": ""
+ },
+ "require": {
+ "firebase/php-jwt": "^6.0",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/psr7": "^2.4.5",
+ "php": "^8.0",
+ "psr/cache": "^2.0||^3.0",
+ "psr/http-message": "^1.1||^2.0",
+ "psr/log": "^3.0"
+ },
+ "require-dev": {
+ "guzzlehttp/promises": "^2.0",
+ "kelvinmo/simplejwt": "0.7.1",
+ "phpseclib/phpseclib": "^3.0.35",
+ "phpspec/prophecy-phpunit": "^2.1",
+ "phpunit/phpunit": "^9.6",
+ "sebastian/comparator": ">=1.2.3",
+ "squizlabs/php_codesniffer": "^3.5",
+ "symfony/process": "^6.0||^7.0",
+ "webmozart/assert": "^1.11"
+ },
+ "suggest": {
+ "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings or for token management. Please require version ^2."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Auth\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google Auth Library for PHP",
+ "homepage": "https://github.com/google/google-auth-library-php",
+ "keywords": [
+ "Authentication",
+ "google",
+ "oauth2"
+ ],
+ "support": {
+ "docs": "https://cloud.google.com/php/docs/reference/auth/latest",
+ "issues": "https://github.com/googleapis/google-auth-library-php/issues",
+ "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.46.0"
+ },
+ "time": "2025-02-12T22:21:37+00:00"
+ },
+ {
+ "name": "google/cloud-tasks",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-cloud-php-tasks.git",
+ "reference": "63d4d2df4612fcf667620b311b048ff5b92e0a42"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-cloud-php-tasks/zipball/63d4d2df4612fcf667620b311b048ff5b92e0a42",
+ "reference": "63d4d2df4612fcf667620b311b048ff5b92e0a42",
+ "shasum": ""
+ },
+ "require": {
+ "google/gax": "^1.36.0",
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "google/cloud-core": "^1.52.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "suggest": {
+ "ext-grpc": "Enables use of gRPC, a universal high-performance RPC framework created by Google.",
+ "ext-protobuf": "Provides a significant increase in throughput over the pure PHP protobuf implementation. See https://cloud.google.com/php/grpc for installation instructions."
+ },
+ "type": "library",
+ "extra": {
+ "component": {
+ "id": "cloud-tasks",
+ "path": "Tasks",
+ "entry": null,
+ "target": "googleapis/google-cloud-php-tasks.git"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Google\\Cloud\\Tasks\\": "src",
+ "GPBMetadata\\Google\\Cloud\\Tasks\\": "metadata"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google Cloud Tasks Client for PHP",
+ "support": {
+ "source": "https://github.com/googleapis/google-cloud-php-tasks/tree/v2.0.1"
+ },
+ "time": "2025-01-24T21:24:06+00:00"
+ },
+ {
+ "name": "google/common-protos",
+ "version": "4.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/common-protos-php.git",
+ "reference": "2554ed1f09aa20faae7b71b590e7063df97ff670"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/common-protos-php/zipball/2554ed1f09aa20faae7b71b590e7063df97ff670",
+ "reference": "2554ed1f09aa20faae7b71b590e7063df97ff670",
+ "shasum": ""
+ },
+ "require": {
+ "google/protobuf": "^v3.25.3||^4.26.1",
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "library",
+ "extra": {
+ "component": {
+ "id": "common-protos",
+ "path": "CommonProtos",
+ "entry": "README.md",
+ "target": "googleapis/common-protos-php.git"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Google\\Api\\": "src/Api",
+ "Google\\Iam\\": "src/Iam",
+ "Google\\Rpc\\": "src/Rpc",
+ "Google\\Type\\": "src/Type",
+ "Google\\Cloud\\": "src/Cloud",
+ "GPBMetadata\\Google\\Api\\": "metadata/Api",
+ "GPBMetadata\\Google\\Iam\\": "metadata/Iam",
+ "GPBMetadata\\Google\\Rpc\\": "metadata/Rpc",
+ "GPBMetadata\\Google\\Type\\": "metadata/Type",
+ "GPBMetadata\\Google\\Cloud\\": "metadata/Cloud",
+ "GPBMetadata\\Google\\Logging\\": "metadata/Logging"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google API Common Protos for PHP",
+ "homepage": "https://github.com/googleapis/common-protos-php",
+ "keywords": [
+ "google"
+ ],
+ "support": {
+ "source": "https://github.com/googleapis/common-protos-php/tree/v4.11.0"
+ },
+ "time": "2025-02-18T19:46:55+00:00"
+ },
+ {
+ "name": "google/gax",
+ "version": "v1.36.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/gax-php.git",
+ "reference": "140599cf5eae2432363ce6198e9fdff851625a7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/gax-php/zipball/140599cf5eae2432363ce6198e9fdff851625a7a",
+ "reference": "140599cf5eae2432363ce6198e9fdff851625a7a",
+ "shasum": ""
+ },
+ "require": {
+ "google/auth": "^1.45",
+ "google/common-protos": "^4.4",
+ "google/grpc-gcp": "^0.4",
+ "google/longrunning": "~0.4",
+ "google/protobuf": "^v3.25.3||^4.26.1",
+ "grpc/grpc": "^1.13",
+ "guzzlehttp/promises": "^2.0",
+ "guzzlehttp/psr7": "^2.0",
+ "php": "^8.0",
+ "ramsey/uuid": "^4.0"
+ },
+ "conflict": {
+ "ext-protobuf": "<3.7.0"
+ },
+ "require-dev": {
+ "phpspec/prophecy-phpunit": "^2.1",
+ "phpstan/phpstan": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "squizlabs/php_codesniffer": "3.*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\ApiCore\\": "src",
+ "GPBMetadata\\ApiCore\\": "metadata/ApiCore"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Google API Core for PHP",
+ "homepage": "https://github.com/googleapis/gax-php",
+ "keywords": [
+ "google"
+ ],
+ "support": {
+ "issues": "https://github.com/googleapis/gax-php/issues",
+ "source": "https://github.com/googleapis/gax-php/tree/v1.36.0"
+ },
+ "time": "2024-12-11T02:47:43+00:00"
+ },
+ {
+ "name": "google/grpc-gcp",
+ "version": "v0.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/grpc-gcp-php.git",
+ "reference": "e585b7721bbe806ef45b5c52ae43dfc2bff89968"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GoogleCloudPlatform/grpc-gcp-php/zipball/e585b7721bbe806ef45b5c52ae43dfc2bff89968",
+ "reference": "e585b7721bbe806ef45b5c52ae43dfc2bff89968",
+ "shasum": ""
+ },
+ "require": {
+ "google/auth": "^1.3",
+ "google/protobuf": "^v3.25.3||^4.26.1",
+ "grpc/grpc": "^v1.13.0",
+ "php": "^8.0",
+ "psr/cache": "^1.0.1||^2.0.0||^3.0.0"
+ },
+ "require-dev": {
+ "google/cloud-spanner": "^1.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Grpc\\Gcp\\": "src/"
+ },
+ "classmap": [
+ "src/generated/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "gRPC GCP library for channel management",
+ "support": {
+ "issues": "https://github.com/GoogleCloudPlatform/grpc-gcp-php/issues",
+ "source": "https://github.com/GoogleCloudPlatform/grpc-gcp-php/tree/v0.4.1"
+ },
+ "time": "2025-02-19T21:53:22+00:00"
+ },
+ {
+ "name": "google/longrunning",
+ "version": "0.4.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/php-longrunning.git",
+ "reference": "624cabb874c10e5ddc9034c999f724894b70a3d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/php-longrunning/zipball/624cabb874c10e5ddc9034c999f724894b70a3d3",
+ "reference": "624cabb874c10e5ddc9034c999f724894b70a3d3",
+ "shasum": ""
+ },
+ "require-dev": {
+ "google/gax": "^1.36.0",
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "component": {
+ "id": "longrunning",
+ "path": "LongRunning",
+ "entry": null,
+ "target": "googleapis/php-longrunning"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Google\\LongRunning\\": "src/LongRunning",
+ "Google\\ApiCore\\LongRunning\\": "src/ApiCore/LongRunning",
+ "GPBMetadata\\Google\\Longrunning\\": "metadata/Longrunning"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google LongRunning Client for PHP",
+ "support": {
+ "source": "https://github.com/googleapis/php-longrunning/tree/v0.4.7"
+ },
+ "time": "2025-01-24T21:24:06+00:00"
+ },
+ {
+ "name": "google/protobuf",
+ "version": "v4.29.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/protocolbuffers/protobuf-php.git",
+ "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7",
+ "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=5.0.0"
+ },
+ "suggest": {
+ "ext-bcmath": "Need to support JSON deserialization"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Protobuf\\": "src/Google/Protobuf",
+ "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "proto library for PHP",
+ "homepage": "https://developers.google.com/protocol-buffers/",
+ "keywords": [
+ "proto"
+ ],
+ "support": {
+ "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.3"
+ },
+ "time": "2025-01-08T21:00:13+00:00"
+ },
+ {
+ "name": "grpc/grpc",
+ "version": "1.57.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/grpc/grpc-php.git",
+ "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/grpc/grpc-php/zipball/b610c42022ed3a22f831439cb93802f2a4502fdf",
+ "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "google/auth": "^v1.3.0"
+ },
+ "suggest": {
+ "ext-protobuf": "For better performance, install the protobuf C extension.",
+ "google/protobuf": "To get started using grpc quickly, install the native protobuf library."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Grpc\\": "src/lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "gRPC library for PHP",
+ "homepage": "https://grpc.io/",
+ "keywords": [
+ "rpc"
+ ],
+ "support": {
+ "source": "https://github.com/grpc/grpc-php/tree/v1.57.0"
+ },
+ "time": "2023-08-14T23:57:54+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.9.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+ "guzzlehttp/psr7": "^2.7.0",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.2",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-24T11:22:20+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
+ "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-10-17T10:06:22+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu/"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-18T11:15:46+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/3.0.0"
+ },
+ "time": "2021-02-03T23:26:27+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "ramsey/collection",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
+ "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "captainhook/plugin-composer": "^5.3",
+ "ergebnis/composer-normalize": "^2.28.3",
+ "fakerphp/faker": "^1.21",
+ "hamcrest/hamcrest-php": "^2.0",
+ "jangregor/phpstan-prophecy": "^1.0",
+ "mockery/mockery": "^1.5",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3",
+ "phpcsstandards/phpcsutils": "^1.0.0-rc1",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan": "^1.9",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5",
+ "psalm/plugin-mockery": "^1.1",
+ "psalm/plugin-phpunit": "^0.18.4",
+ "ramsey/coding-standard": "^2.0.3",
+ "ramsey/conventional-commits": "^1.3",
+ "vimeo/psalm": "^5.4"
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ },
+ "ramsey/conventional-commits": {
+ "configFile": "conventional-commits.json"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ramsey\\Collection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com/"
+ }
+ ],
+ "description": "A PHP library for representing and manipulating collections.",
+ "keywords": [
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-31T21:50:55+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "4.7.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+ "ext-json": "*",
+ "php": "^8.0",
+ "ramsey/collection": "^1.2 || ^2.0"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "captainhook/captainhook": "^5.10",
+ "captainhook/plugin-composer": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "doctrine/annotations": "^1.8",
+ "ergebnis/composer-normalize": "^2.15",
+ "mockery/mockery": "^1.3",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock": "^2.2",
+ "php-mock/php-mock-mockery": "^1.3",
+ "php-parallel-lint/php-parallel-lint": "^1.1",
+ "phpbench/phpbench": "^1.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9",
+ "ramsey/composer-repl": "^1.4",
+ "slevomat/coding-standard": "^8.4",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.9"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-27T21:32:50+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "thecodingmachine/safe",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thecodingmachine/safe.git",
+ "reference": "22ffad3248982a784f9870a37aeb2e522bd19645"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/22ffad3248982a784f9870a37aeb2e522bd19645",
+ "reference": "22ffad3248982a784f9870a37aeb2e522bd19645",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpstan/phpstan": "^2",
+ "phpunit/phpunit": "^10",
+ "squizlabs/php_codesniffer": "^3.2"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "lib/special_cases.php",
+ "generated/apache.php",
+ "generated/apcu.php",
+ "generated/array.php",
+ "generated/bzip2.php",
+ "generated/calendar.php",
+ "generated/classobj.php",
+ "generated/com.php",
+ "generated/cubrid.php",
+ "generated/curl.php",
+ "generated/datetime.php",
+ "generated/dir.php",
+ "generated/eio.php",
+ "generated/errorfunc.php",
+ "generated/exec.php",
+ "generated/fileinfo.php",
+ "generated/filesystem.php",
+ "generated/filter.php",
+ "generated/fpm.php",
+ "generated/ftp.php",
+ "generated/funchand.php",
+ "generated/gettext.php",
+ "generated/gmp.php",
+ "generated/gnupg.php",
+ "generated/hash.php",
+ "generated/ibase.php",
+ "generated/ibmDb2.php",
+ "generated/iconv.php",
+ "generated/image.php",
+ "generated/imap.php",
+ "generated/info.php",
+ "generated/inotify.php",
+ "generated/json.php",
+ "generated/ldap.php",
+ "generated/libxml.php",
+ "generated/lzf.php",
+ "generated/mailparse.php",
+ "generated/mbstring.php",
+ "generated/misc.php",
+ "generated/mysql.php",
+ "generated/mysqli.php",
+ "generated/network.php",
+ "generated/oci8.php",
+ "generated/opcache.php",
+ "generated/openssl.php",
+ "generated/outcontrol.php",
+ "generated/pcntl.php",
+ "generated/pcre.php",
+ "generated/pgsql.php",
+ "generated/posix.php",
+ "generated/ps.php",
+ "generated/pspell.php",
+ "generated/readline.php",
+ "generated/rnp.php",
+ "generated/rpminfo.php",
+ "generated/rrd.php",
+ "generated/sem.php",
+ "generated/session.php",
+ "generated/shmop.php",
+ "generated/sockets.php",
+ "generated/sodium.php",
+ "generated/solr.php",
+ "generated/spl.php",
+ "generated/sqlsrv.php",
+ "generated/ssdeep.php",
+ "generated/ssh2.php",
+ "generated/stream.php",
+ "generated/strings.php",
+ "generated/swoole.php",
+ "generated/uodbc.php",
+ "generated/uopz.php",
+ "generated/url.php",
+ "generated/var.php",
+ "generated/xdiff.php",
+ "generated/xml.php",
+ "generated/xmlrpc.php",
+ "generated/yaml.php",
+ "generated/yaz.php",
+ "generated/zip.php",
+ "generated/zlib.php"
+ ],
+ "classmap": [
+ "lib/DateTime.php",
+ "lib/DateTimeImmutable.php",
+ "lib/Exceptions/",
+ "generated/Exceptions/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHP core functions that throw exceptions instead of returning FALSE on error",
+ "support": {
+ "issues": "https://github.com/thecodingmachine/safe/issues",
+ "source": "https://github.com/thecodingmachine/safe/tree/v3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/OskarStark",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/shish",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-19T19:23:00+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "carbonphp/carbon-doctrine-types",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "conflict": {
+ "doctrine/dbal": "<4.0.0 || >=5.0.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^4.0.0",
+ "nesbot/carbon": "^2.71.0 || ^3.0.0",
+ "phpunit/phpunit": "^10.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "KyleKatarn",
+ "email": "kylekatarnls@gmail.com"
+ }
+ ],
+ "description": "Types to use Carbon in Doctrine",
+ "keywords": [
+ "carbon",
+ "date",
+ "datetime",
+ "doctrine",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+ "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-09T16:56:22+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
+ "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de/"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be/"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl/"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com/",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-19T14:15:21+00:00"
+ },
+ {
+ "name": "dflydev/dot-access-data",
+ "version": "v3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com/"
+ },
+ {
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com/"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com/"
+ }
+ ],
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+ "keywords": [
+ "access",
+ "data",
+ "dot",
+ "notation"
+ ],
+ "support": {
+ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+ "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
+ },
+ "time": "2024-07-08T12:26:09+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+ "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11.0",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "vimeo/psalm": "^4.25 || ^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.10"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-18T20:23:39+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^10.5",
+ "psalm/plugin-phpunit": "^0.18.3",
+ "vimeo/psalm": "^5.21"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-05T11:56:58+00:00"
+ },
+ {
+ "name": "dragonmantank/cron-expression",
+ "version": "v3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dragonmantank/cron-expression.git",
+ "reference": "8c784d071debd117328803d86b2097615b457500"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500",
+ "reference": "8c784d071debd117328803d86b2097615b457500",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0",
+ "webmozart/assert": "^1.0"
+ },
+ "replace": {
+ "mtdowling/cron-expression": "^1.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^1.0",
+ "phpunit/phpunit": "^7.0|^8.0|^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Cron\\": "src/Cron/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Tankersley",
+ "email": "chris@ctankersley.com",
+ "homepage": "https://github.com/dragonmantank"
+ }
+ ],
+ "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+ "keywords": [
+ "cron",
+ "schedule"
+ ],
+ "support": {
+ "issues": "https://github.com/dragonmantank/cron-expression/issues",
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/dragonmantank",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T13:47:03+00:00"
+ },
+ {
+ "name": "egulias/email-validator",
+ "version": "4.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "b115554301161fa21467629f1e1391c1936de517"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517",
+ "reference": "b115554301161fa21467629f1e1391c1936de517",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2.0 || ^3.0",
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.26"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.2",
+ "vimeo/psalm": "^5.12"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Egulias\\EmailValidator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eduardo Gulias Davis"
+ }
+ ],
+ "description": "A library for validating emails against several RFCs",
+ "homepage": "https://github.com/egulias/EmailValidator",
+ "keywords": [
+ "email",
+ "emailvalidation",
+ "emailvalidator",
+ "validation",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/4.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-27T00:36:43+00:00"
+ },
+ {
+ "name": "fakerphp/faker",
+ "version": "v1.24.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "psr/container": "^1.0 || ^2.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "conflict": {
+ "fzaninotto/faker": "*"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "doctrine/persistence": "^1.3 || ^2.0",
+ "ext-intl": "*",
+ "phpunit/phpunit": "^9.5.26",
+ "symfony/phpunit-bridge": "^5.4.16"
+ },
+ "suggest": {
+ "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+ "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+ "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+ "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+ "ext-mbstring": "Required for multibyte Unicode string functionality."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Faker\\": "src/Faker/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "François Zaninotto"
+ }
+ ],
+ "description": "Faker is a PHP library that generates fake data for you.",
+ "keywords": [
+ "data",
+ "faker",
+ "fixtures"
+ ],
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1"
+ },
+ "time": "2024-11-21T13:46:39+00:00"
+ },
+ {
+ "name": "filp/whoops",
+ "version": "2.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/filp/whoops.git",
+ "reference": "075bc0c26631110584175de6523ab3f1652eb28e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e",
+ "reference": "075bc0c26631110584175de6523ab3f1652eb28e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0",
+ "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.0",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3",
+ "symfony/var-dumper": "^4.0 || ^5.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "description": "php error handling for cool kids",
+ "homepage": "https://filp.github.io/whoops/",
+ "keywords": [
+ "error",
+ "exception",
+ "handling",
+ "library",
+ "throwable",
+ "whoops"
+ ],
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-25T12:00:00+00:00"
+ },
+ {
+ "name": "fruitcake/php-cors",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruitcake/php-cors.git",
+ "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b",
+ "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0",
+ "symfony/http-foundation": "^4.4|^5.4|^6|^7"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.4",
+ "phpunit/phpunit": "^9",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Fruitcake\\Cors\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fruitcake",
+ "homepage": "https://fruitcake.nl/"
+ },
+ {
+ "name": "Barryvdh",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "Cross-origin resource sharing library for the Symfony HttpFoundation",
+ "homepage": "https://github.com/fruitcake/php-cors",
+ "keywords": [
+ "cors",
+ "laravel",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/fruitcake/php-cors/issues",
+ "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://fruitcake.nl/",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/barryvdh",
+ "type": "github"
+ }
+ ],
+ "time": "2023-10-12T05:21:21+00:00"
+ },
+ {
+ "name": "graham-campbell/result-type",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:45:45+00:00"
+ },
+ {
+ "name": "guzzlehttp/uri-template",
+ "version": "v1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/uri-template.git",
+ "reference": "30e286560c137526eccd4ce21b2de477ab0676d2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2",
+ "reference": "30e286560c137526eccd4ce21b2de477ab0676d2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "symfony/polyfill-php80": "^1.24"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
+ "uri-template/tests": "1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\UriTemplate\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "description": "A polyfill class for uri_template of PHP",
+ "keywords": [
+ "guzzlehttp",
+ "uri-template"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/uri-template/issues",
+ "source": "https://github.com/guzzle/uri-template/tree/v1.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-03T10:55:03+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3|^7.0|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
+ "time": "2020-07-09T08:09:16+00:00"
+ },
+ {
+ "name": "larastan/larastan",
+ "version": "v3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/larastan/larastan.git",
+ "reference": "dbb2dc20e5c8e1ed3ff289054e1955f269187312"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/larastan/larastan/zipball/dbb2dc20e5c8e1ed3ff289054e1955f269187312",
+ "reference": "dbb2dc20e5c8e1ed3ff289054e1955f269187312",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/console": "^11.15.0 || ^12.0",
+ "illuminate/container": "^11.15.0 || ^12.0",
+ "illuminate/contracts": "^11.15.0 || ^12.0",
+ "illuminate/database": "^11.15.0 || ^12.0",
+ "illuminate/http": "^11.15.0 || ^12.0",
+ "illuminate/pipeline": "^11.15.0 || ^12.0",
+ "illuminate/support": "^11.15.0 || ^12.0",
+ "php": "^8.2",
+ "phpmyadmin/sql-parser": "^5.9.0",
+ "phpstan/phpstan": "^2.1.3"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0",
+ "laravel/framework": "^11.15.0 || ^12.0",
+ "mockery/mockery": "^1.6",
+ "nikic/php-parser": "^5.3",
+ "orchestra/canvas": "^v9.1.3 || ^10.0",
+ "orchestra/testbench-core": "^9.5.2 || ^10.0",
+ "phpstan/phpstan-deprecation-rules": "^2.0.0",
+ "phpunit/phpunit": "^10.5.35 || ^11.3.6"
+ },
+ "suggest": {
+ "orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Larastan\\Larastan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Can Vural",
+ "email": "can9119@gmail.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel",
+ "keywords": [
+ "PHPStan",
+ "code analyse",
+ "code analysis",
+ "larastan",
+ "laravel",
+ "package",
+ "php",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/larastan/larastan/issues",
+ "source": "https://github.com/larastan/larastan/tree/v3.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/canvural",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-20T15:25:15+00:00"
+ },
+ {
+ "name": "laravel/framework",
+ "version": "v12.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/framework.git",
+ "reference": "d99e2385a6d4324782d52f4423891966425641be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/d99e2385a6d4324782d52f4423891966425641be",
+ "reference": "d99e2385a6d4324782d52f4423891966425641be",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.11|^0.12",
+ "composer-runtime-api": "^2.2",
+ "doctrine/inflector": "^2.0.5",
+ "dragonmantank/cron-expression": "^3.4",
+ "egulias/email-validator": "^3.2.1|^4.0",
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-session": "*",
+ "ext-tokenizer": "*",
+ "fruitcake/php-cors": "^1.3",
+ "guzzlehttp/guzzle": "^7.8.2",
+ "guzzlehttp/uri-template": "^1.0",
+ "laravel/prompts": "^0.3.0",
+ "laravel/serializable-closure": "^1.3|^2.0",
+ "league/commonmark": "^2.6",
+ "league/flysystem": "^3.25.1",
+ "league/flysystem-local": "^3.25.1",
+ "league/uri": "^7.5.1",
+ "monolog/monolog": "^3.0",
+ "nesbot/carbon": "^3.8.4",
+ "nunomaduro/termwind": "^2.0",
+ "php": "^8.2",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/log": "^1.0|^2.0|^3.0",
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "ramsey/uuid": "^4.7",
+ "symfony/console": "^7.2.0",
+ "symfony/error-handler": "^7.2.0",
+ "symfony/finder": "^7.2.0",
+ "symfony/http-foundation": "^7.2.0",
+ "symfony/http-kernel": "^7.2.0",
+ "symfony/mailer": "^7.2.0",
+ "symfony/mime": "^7.2.0",
+ "symfony/polyfill-php83": "^1.31",
+ "symfony/process": "^7.2.0",
+ "symfony/routing": "^7.2.0",
+ "symfony/uid": "^7.2.0",
+ "symfony/var-dumper": "^7.2.0",
+ "tijsverkoyen/css-to-inline-styles": "^2.2.5",
+ "vlucas/phpdotenv": "^5.6.1",
+ "voku/portable-ascii": "^2.0.2"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "psr/log-implementation": "1.0|2.0|3.0",
+ "psr/simple-cache-implementation": "1.0|2.0|3.0"
+ },
+ "replace": {
+ "illuminate/auth": "self.version",
+ "illuminate/broadcasting": "self.version",
+ "illuminate/bus": "self.version",
+ "illuminate/cache": "self.version",
+ "illuminate/collections": "self.version",
+ "illuminate/concurrency": "self.version",
+ "illuminate/conditionable": "self.version",
+ "illuminate/config": "self.version",
+ "illuminate/console": "self.version",
+ "illuminate/container": "self.version",
+ "illuminate/contracts": "self.version",
+ "illuminate/cookie": "self.version",
+ "illuminate/database": "self.version",
+ "illuminate/encryption": "self.version",
+ "illuminate/events": "self.version",
+ "illuminate/filesystem": "self.version",
+ "illuminate/hashing": "self.version",
+ "illuminate/http": "self.version",
+ "illuminate/log": "self.version",
+ "illuminate/macroable": "self.version",
+ "illuminate/mail": "self.version",
+ "illuminate/notifications": "self.version",
+ "illuminate/pagination": "self.version",
+ "illuminate/pipeline": "self.version",
+ "illuminate/process": "self.version",
+ "illuminate/queue": "self.version",
+ "illuminate/redis": "self.version",
+ "illuminate/routing": "self.version",
+ "illuminate/session": "self.version",
+ "illuminate/support": "self.version",
+ "illuminate/testing": "self.version",
+ "illuminate/translation": "self.version",
+ "illuminate/validation": "self.version",
+ "illuminate/view": "self.version",
+ "spatie/once": "*"
+ },
+ "require-dev": {
+ "ably/ably-php": "^1.0",
+ "aws/aws-sdk-php": "^3.322.9",
+ "ext-gmp": "*",
+ "fakerphp/faker": "^1.24",
+ "guzzlehttp/promises": "^2.0.3",
+ "guzzlehttp/psr7": "^2.4",
+ "laravel/pint": "^1.18",
+ "league/flysystem-aws-s3-v3": "^3.25.1",
+ "league/flysystem-ftp": "^3.25.1",
+ "league/flysystem-path-prefixing": "^3.25.1",
+ "league/flysystem-read-only": "^3.25.1",
+ "league/flysystem-sftp-v3": "^3.25.1",
+ "mockery/mockery": "^1.6.10",
+ "orchestra/testbench-core": "^10.0",
+ "pda/pheanstalk": "^5.0.6",
+ "php-http/discovery": "^1.15",
+ "phpstan/phpstan": "^2.0",
+ "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
+ "predis/predis": "^2.3",
+ "resend/resend-php": "^0.10.0",
+ "symfony/cache": "^7.2.0",
+ "symfony/http-client": "^7.2.0",
+ "symfony/psr-http-message-bridge": "^7.2.0",
+ "symfony/translation": "^7.2.0"
+ },
+ "suggest": {
+ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).",
+ "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).",
+ "ext-apcu": "Required to use the APC cache driver.",
+ "ext-fileinfo": "Required to use the Filesystem class.",
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.",
+ "ext-pdo": "Required to use all database features.",
+ "ext-posix": "Required to use all features of the queue worker.",
+ "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+ "laravel/tinker": "Required to use the tinker console command (^2.0).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
+ "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).",
+ "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).",
+ "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
+ "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
+ "mockery/mockery": "Required to use mocking (^1.6).",
+ "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
+ "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
+ "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).",
+ "predis/predis": "Required to use the predis connector (^2.3).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
+ "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).",
+ "symfony/cache": "Required to PSR-6 cache bridge (^7.2).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).",
+ "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).",
+ "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).",
+ "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).",
+ "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Illuminate/Collections/functions.php",
+ "src/Illuminate/Collections/helpers.php",
+ "src/Illuminate/Events/functions.php",
+ "src/Illuminate/Filesystem/functions.php",
+ "src/Illuminate/Foundation/helpers.php",
+ "src/Illuminate/Log/functions.php",
+ "src/Illuminate/Support/functions.php",
+ "src/Illuminate/Support/helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\": "src/Illuminate/",
+ "Illuminate\\Support\\": [
+ "src/Illuminate/Macroable/",
+ "src/Illuminate/Collections/",
+ "src/Illuminate/Conditionable/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Laravel Framework.",
+ "homepage": "https://laravel.com/",
+ "keywords": [
+ "framework",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-02-24T13:31:23+00:00"
+ },
+ {
+ "name": "laravel/legacy-factories",
+ "version": "v1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/legacy-factories.git",
+ "reference": "cd0f8c77d116bac121e9779fcff1f71801aaac50"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/cd0f8c77d116bac121e9779fcff1f71801aaac50",
+ "reference": "cd0f8c77d116bac121e9779fcff1f71801aaac50",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/macroable": "^8.0|^9.0|^10.0|^11.0|^12.0",
+ "php": "^7.3|^8.0",
+ "symfony/finder": "^3.4|^4.0|^5.0|^6.0|^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Database\\Eloquent\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The legacy version of the Laravel Eloquent factories.",
+ "homepage": "http://laravel.com/",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-01-24T15:41:36+00:00"
+ },
+ {
+ "name": "laravel/pail",
+ "version": "v1.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/pail.git",
+ "reference": "f31f4980f52be17c4667f3eafe034e6826787db2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/pail/zipball/f31f4980f52be17c4667f3eafe034e6826787db2",
+ "reference": "f31f4980f52be17c4667f3eafe034e6826787db2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "illuminate/console": "^10.24|^11.0|^12.0",
+ "illuminate/contracts": "^10.24|^11.0|^12.0",
+ "illuminate/log": "^10.24|^11.0|^12.0",
+ "illuminate/process": "^10.24|^11.0|^12.0",
+ "illuminate/support": "^10.24|^11.0|^12.0",
+ "nunomaduro/termwind": "^1.15|^2.0",
+ "php": "^8.2",
+ "symfony/console": "^6.0|^7.0"
+ },
+ "require-dev": {
+ "laravel/framework": "^10.24|^11.0|^12.0",
+ "laravel/pint": "^1.13",
+ "orchestra/testbench-core": "^8.13|^9.0|^10.0",
+ "pestphp/pest": "^2.20|^3.0",
+ "pestphp/pest-plugin-type-coverage": "^2.3|^3.0",
+ "phpstan/phpstan": "^1.10",
+ "symfony/var-dumper": "^6.3|^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Pail\\PailServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Pail\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Easily delve into your Laravel application's log files directly from the command line.",
+ "homepage": "https://github.com/laravel/pail",
+ "keywords": [
+ "laravel",
+ "logs",
+ "php",
+ "tail"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/pail/issues",
+ "source": "https://github.com/laravel/pail"
+ },
+ "time": "2025-01-28T15:15:15+00:00"
+ },
+ {
+ "name": "laravel/pint",
+ "version": "v1.21.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/pint.git",
+ "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425",
+ "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-tokenizer": "*",
+ "ext-xml": "*",
+ "php": "^8.2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.68.5",
+ "illuminate/view": "^11.42.0",
+ "larastan/larastan": "^3.0.4",
+ "laravel-zero/framework": "^11.36.1",
+ "mockery/mockery": "^1.6.12",
+ "nunomaduro/termwind": "^2.3",
+ "pestphp/pest": "^2.36.0"
+ },
+ "bin": [
+ "builds/pint"
+ ],
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "App\\": "app/",
+ "Database\\Seeders\\": "database/seeders/",
+ "Database\\Factories\\": "database/factories/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "An opinionated code formatter for PHP.",
+ "homepage": "https://laravel.com/",
+ "keywords": [
+ "format",
+ "formatter",
+ "lint",
+ "linter",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/pint/issues",
+ "source": "https://github.com/laravel/pint"
+ },
+ "time": "2025-02-18T03:18:57+00:00"
+ },
+ {
+ "name": "laravel/prompts",
+ "version": "v0.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/prompts.git",
+ "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1",
+ "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "ext-mbstring": "*",
+ "php": "^8.1",
+ "symfony/console": "^6.2|^7.0"
+ },
+ "conflict": {
+ "illuminate/console": ">=10.17.0 <10.25.0",
+ "laravel/framework": ">=10.17.0 <10.25.0"
+ },
+ "require-dev": {
+ "illuminate/collections": "^10.0|^11.0|^12.0",
+ "mockery/mockery": "^1.5",
+ "pestphp/pest": "^2.3|^3.4",
+ "phpstan/phpstan": "^1.11",
+ "phpstan/phpstan-mockery": "^1.1"
+ },
+ "suggest": {
+ "ext-pcntl": "Required for the spinner to be animated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.3.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Laravel\\Prompts\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Add beautiful and user-friendly forms to your command-line applications.",
+ "support": {
+ "issues": "https://github.com/laravel/prompts/issues",
+ "source": "https://github.com/laravel/prompts/tree/v0.3.5"
+ },
+ "time": "2025-02-11T13:34:40+00:00"
+ },
+ {
+ "name": "laravel/serializable-closure",
+ "version": "v2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/serializable-closure.git",
+ "reference": "f379c13663245f7aa4512a7869f62eb14095f23f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f379c13663245f7aa4512a7869f62eb14095f23f",
+ "reference": "f379c13663245f7aa4512a7869f62eb14095f23f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "illuminate/support": "^10.0|^11.0|^12.0",
+ "nesbot/carbon": "^2.67|^3.0",
+ "pestphp/pest": "^2.36|^3.0",
+ "phpstan/phpstan": "^2.0",
+ "symfony/var-dumper": "^6.2.0|^7.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\SerializableClosure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "nuno@laravel.com"
+ }
+ ],
+ "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
+ "keywords": [
+ "closure",
+ "laravel",
+ "serializable"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/serializable-closure/issues",
+ "source": "https://github.com/laravel/serializable-closure"
+ },
+ "time": "2025-02-11T15:03:05+00:00"
+ },
+ {
+ "name": "laravel/tinker",
+ "version": "v2.10.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/tinker.git",
+ "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3",
+ "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "php": "^7.2.5|^8.0",
+ "psy/psysh": "^0.11.1|^0.12.0",
+ "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.3|^1.4.2",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0"
+ },
+ "suggest": {
+ "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Tinker\\TinkerServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Tinker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Powerful REPL for the Laravel framework.",
+ "keywords": [
+ "REPL",
+ "Tinker",
+ "laravel",
+ "psysh"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/tinker/issues",
+ "source": "https://github.com/laravel/tinker/tree/v2.10.1"
+ },
+ "time": "2025-01-27T14:24:01+00:00"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "2.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "d990688c91cedfb69753ffc2512727ec646df2ad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad",
+ "reference": "d990688c91cedfb69753ffc2512727ec646df2ad",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "php": "^7.4 || ^8.0",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0"
+ },
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com/",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com/",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-29T14:10:59+00:00"
+ },
+ {
+ "name": "league/config",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/config.git",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "shasum": ""
+ },
+ "require": {
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com/",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "homepage": "https://config.thephpleague.com/",
+ "keywords": [
+ "array",
+ "config",
+ "configuration",
+ "dot",
+ "dot-access",
+ "nested",
+ "schema"
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-11T20:36:23+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "3.29.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319",
+ "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem-local": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "conflict": {
+ "async-aws/core": "<1.19.0",
+ "async-aws/s3": "<1.14.0",
+ "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+ "guzzlehttp/guzzle": "<7.0",
+ "guzzlehttp/ringphp": "<1.1.1",
+ "phpseclib/phpseclib": "3.0.15",
+ "symfony/http-client": "<5.2"
+ },
+ "require-dev": {
+ "async-aws/s3": "^1.5 || ^2.0",
+ "async-aws/simple-s3": "^1.1 || ^2.0",
+ "aws/aws-sdk-php": "^3.295.10",
+ "composer/semver": "^3.0",
+ "ext-fileinfo": "*",
+ "ext-ftp": "*",
+ "ext-mongodb": "^1.3",
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.5",
+ "google/cloud-storage": "^1.23",
+ "guzzlehttp/psr7": "^2.6",
+ "microsoft/azure-storage-blob": "^1.1",
+ "mongodb/mongodb": "^1.2",
+ "phpseclib/phpseclib": "^3.0.36",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^9.5.11|^10.0",
+ "sabre/dav": "^4.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "File storage abstraction for PHP",
+ "keywords": [
+ "WebDAV",
+ "aws",
+ "cloud",
+ "file",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/3.29.1"
+ },
+ "time": "2024-10-08T08:58:34+00:00"
+ },
+ {
+ "name": "league/flysystem-local",
+ "version": "3.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-local.git",
+ "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27",
+ "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/flysystem": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Local\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Local filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "local"
+ ],
+ "support": {
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0"
+ },
+ "time": "2024-08-09T21:24:39+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9",
+ "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-21T08:32:55+00:00"
+ },
+ {
+ "name": "league/uri",
+ "version": "7.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri.git",
+ "reference": "81fb5145d2644324614cc532b28efd0215bda430"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430",
+ "reference": "81fb5145d2644324614cc532b28efd0215bda430",
+ "shasum": ""
+ },
+ "require": {
+ "league/uri-interfaces": "^7.5",
+ "php": "^8.1"
+ },
+ "conflict": {
+ "league/uri-schemes": "^1.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-fileinfo": "to create Data URI from file contennts",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain",
+ "league/uri-components": "Needed to easily manipulate URI objects components",
+ "php-64bit": "to improve IPV4 host parsing",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com/"
+ }
+ ],
+ "description": "URI manipulation library",
+ "homepage": "https://uri.thephpleague.com/",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "middleware",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "uri-template",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com/",
+ "forum": "https://thephpleague.slack.com/",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri/tree/7.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-08T08:40:02+00:00"
+ },
+ {
+ "name": "league/uri-interfaces",
+ "version": "7.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri-interfaces.git",
+ "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742",
+ "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^8.1",
+ "psr/http-factory": "^1",
+ "psr/http-message": "^1.1 || ^2.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "php-64bit": "to improve IPV4 host parsing",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com/"
+ }
+ ],
+ "description": "Common interfaces and classes for URI representation and interaction",
+ "homepage": "https://uri.thephpleague.com/",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com/",
+ "forum": "https://thephpleague.slack.com/",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-08T08:18:47+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/helpers.php",
+ "library/Mockery.php"
+ ],
+ "psr-4": {
+ "Mockery\\": "library/Mockery"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk/",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
+ },
+ "time": "2024-05-16T03:13:13+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "3.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
+ "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/log": "^2.0 || ^3.0"
+ },
+ "provide": {
+ "psr/log-implementation": "3.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7 || ^8",
+ "ext-json": "*",
+ "graylog2/gelf-php": "^1.4.2 || ^2.0",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/psr7": "^2.2",
+ "mongodb/mongodb": "^1.8",
+ "php-amqplib/php-amqplib": "~2.4 || ^3",
+ "php-console/php-console": "^3.1.8",
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpunit/phpunit": "^10.5.17 || ^11.0.7",
+ "predis/predis": "^1.1 || ^2",
+ "rollbar/rollbar": "^4.0",
+ "ruflin/elastica": "^7 || ^8",
+ "symfony/mailer": "^5.4 || ^6",
+ "symfony/mime": "^5.4 || ^6"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be/"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/3.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-05T17:15:07+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "024473a478be9df5fdaca2c793f2232fe788e414"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
+ "reference": "024473a478be9df5fdaca2c793f2232fe788e414",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-12T12:17:51+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "3.8.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon.git",
+ "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
+ "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
+ "shasum": ""
+ },
+ "require": {
+ "carbonphp/carbon-doctrine-types": "<100.0",
+ "ext-json": "*",
+ "php": "^8.1",
+ "psr/clock": "^1.0",
+ "symfony/clock": "^6.3 || ^7.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6.3 || ^4.0",
+ "doctrine/orm": "^2.15.2 || ^3.0",
+ "friendsofphp/php-cs-fixer": "^3.57.2",
+ "kylekatarnls/multi-tester": "^2.5.3",
+ "ondrejmirtes/better-reflection": "^6.25.0.4",
+ "phpmd/phpmd": "^2.15.0",
+ "phpstan/extension-installer": "^1.3.1",
+ "phpstan/phpstan": "^1.11.2",
+ "phpunit/phpunit": "^10.5.20",
+ "squizlabs/php_codesniffer": "^3.9.0"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev",
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com/"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com/",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/CarbonPHP/carbon/issues",
+ "source": "https://github.com/CarbonPHP/carbon"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-20T17:33:38+00:00"
+ },
+ {
+ "name": "nette/schema",
+ "version": "v1.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/schema.git",
+ "reference": "da801d52f0354f70a638673c4a0f04e16529431d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d",
+ "reference": "da801d52f0354f70a638673c4a0f04e16529431d",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^4.0",
+ "php": "8.1 - 8.4"
+ },
+ "require-dev": {
+ "nette/tester": "^2.5.2",
+ "phpstan/phpstan-nette": "^1.0",
+ "tracy/tracy": "^2.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com/"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "📐 Nette Schema: validating data structures against a given Schema.",
+ "homepage": "https://nette.org/",
+ "keywords": [
+ "config",
+ "nette"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/schema/issues",
+ "source": "https://github.com/nette/schema/tree/v1.3.2"
+ },
+ "time": "2024-10-06T23:10:23+00:00"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.0 - 8.4"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "dev-master",
+ "nette/tester": "^2.5",
+ "phpstan/phpstan": "^1.0",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com/"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org/",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.0.5"
+ },
+ "time": "2024-08-07T15:39:19+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "447a020a1f875a434d62f2a401f53b82a396e494"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
+ "reference": "447a020a1f875a434d62f2a401f53b82a396e494",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
+ },
+ "time": "2024-12-30T11:07:19+00:00"
+ },
+ {
+ "name": "nunomaduro/collision",
+ "version": "v8.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/collision.git",
+ "reference": "86f003c132143d5a2ab214e19933946409e0cae7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/86f003c132143d5a2ab214e19933946409e0cae7",
+ "reference": "86f003c132143d5a2ab214e19933946409e0cae7",
+ "shasum": ""
+ },
+ "require": {
+ "filp/whoops": "^2.16.0",
+ "nunomaduro/termwind": "^2.3.0",
+ "php": "^8.2.0",
+ "symfony/console": "^7.2.1"
+ },
+ "conflict": {
+ "laravel/framework": "<11.39.1 || >=13.0.0",
+ "phpunit/phpunit": "<11.5.3 || >=12.0.0"
+ },
+ "require-dev": {
+ "larastan/larastan": "^2.9.12",
+ "laravel/framework": "^11.39.1",
+ "laravel/pint": "^1.20.0",
+ "laravel/sail": "^1.40.0",
+ "laravel/sanctum": "^4.0.7",
+ "laravel/tinker": "^2.10.0",
+ "orchestra/testbench-core": "^9.9.2",
+ "pestphp/pest": "^3.7.3",
+ "sebastian/environment": "^6.1.0 || ^7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-8.x": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "./src/Adapters/Phpunit/Autoload.php"
+ ],
+ "psr-4": {
+ "NunoMaduro\\Collision\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Cli error handling for console/command-line PHP applications.",
+ "keywords": [
+ "artisan",
+ "cli",
+ "command-line",
+ "console",
+ "dev",
+ "error",
+ "handling",
+ "laravel",
+ "laravel-zero",
+ "php",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/collision/issues",
+ "source": "https://github.com/nunomaduro/collision"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2025-01-23T13:41:43+00:00"
+ },
+ {
+ "name": "nunomaduro/termwind",
+ "version": "v2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/termwind.git",
+ "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda",
+ "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^8.2",
+ "symfony/console": "^7.1.8"
+ },
+ "require-dev": {
+ "illuminate/console": "^11.33.2",
+ "laravel/pint": "^1.18.2",
+ "mockery/mockery": "^1.6.12",
+ "pestphp/pest": "^2.36.0",
+ "phpstan/phpstan": "^1.12.11",
+ "phpstan/phpstan-strict-rules": "^1.6.1",
+ "symfony/var-dumper": "^7.1.8",
+ "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Termwind\\Laravel\\TermwindServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Termwind\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Its like Tailwind CSS, but for the console.",
+ "keywords": [
+ "cli",
+ "console",
+ "css",
+ "package",
+ "php",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/termwind/issues",
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/xiCO2k",
+ "type": "github"
+ }
+ ],
+ "time": "2024-11-21T10:39:51+00:00"
+ },
+ {
+ "name": "orchestra/canvas",
+ "version": "v10.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/canvas.git",
+ "reference": "8665e96c254350484ded1cdf158765767abc7075"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/canvas/zipball/8665e96c254350484ded1cdf158765767abc7075",
+ "reference": "8665e96c254350484ded1cdf158765767abc7075",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "composer/semver": "^3.0",
+ "illuminate/console": "^12.0",
+ "illuminate/database": "^12.0",
+ "illuminate/filesystem": "^12.0",
+ "illuminate/support": "^12.0",
+ "orchestra/canvas-core": "^10.0",
+ "orchestra/testbench-core": "^10.0",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.31",
+ "symfony/yaml": "^7.0.3"
+ },
+ "require-dev": {
+ "laravel/framework": "^12.0",
+ "laravel/pint": "^1.20",
+ "mockery/mockery": "^1.6.10",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^11.5.7",
+ "spatie/laravel-ray": "^1.39.1"
+ },
+ "bin": [
+ "canvas"
+ ],
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Orchestra\\Canvas\\LaravelServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Canvas\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Code Generators for Laravel Applications and Packages",
+ "support": {
+ "issues": "https://github.com/orchestral/canvas/issues",
+ "source": "https://github.com/orchestral/canvas/tree/v10.0.1"
+ },
+ "time": "2025-02-15T11:42:39+00:00"
+ },
+ {
+ "name": "orchestra/canvas-core",
+ "version": "v10.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/canvas-core.git",
+ "reference": "22b6515e7a070e1c45c8a3a9819f8b6cb0234173"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/canvas-core/zipball/22b6515e7a070e1c45c8a3a9819f8b6cb0234173",
+ "reference": "22b6515e7a070e1c45c8a3a9819f8b6cb0234173",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "composer/semver": "^3.0",
+ "illuminate/console": "^12.0",
+ "illuminate/support": "^12.0",
+ "orchestra/sidekick": "^1.0.2",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.31"
+ },
+ "require-dev": {
+ "laravel/framework": "^12.0",
+ "laravel/pint": "^1.21",
+ "mockery/mockery": "^1.6.10",
+ "orchestra/testbench-core": "^10.0",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^11.5.7",
+ "symfony/yaml": "^7.2"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Orchestra\\Canvas\\Core\\LaravelServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Canvas\\Core\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Code Generators Builder for Laravel Applications and Packages",
+ "support": {
+ "issues": "https://github.com/orchestral/canvas/issues",
+ "source": "https://github.com/orchestral/canvas-core/tree/v10.0.1"
+ },
+ "time": "2025-02-19T04:17:05+00:00"
+ },
+ {
+ "name": "orchestra/sidekick",
+ "version": "v1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/sidekick.git",
+ "reference": "95e056508a5990480fc9a67cacf1119b58d8d233"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/sidekick/zipball/95e056508a5990480fc9a67cacf1119b58d8d233",
+ "reference": "95e056508a5990480fc9a67cacf1119b58d8d233",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "php": "^8.0",
+ "symfony/polyfill-php83": "^1.31"
+ },
+ "require-dev": {
+ "laravel/framework": "^9.52.16|^10.48.28|^11.42.1|^12.0|^13.0",
+ "laravel/pint": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^9.6|^10.0|^11.0|^12.0",
+ "symfony/process": "^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Orchestra\\Sidekick\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Packages Toolkit Utilities and Helpers for Laravel",
+ "support": {
+ "issues": "https://github.com/orchestral/sidekick/issues",
+ "source": "https://github.com/orchestral/sidekick/tree/v1.0.4"
+ },
+ "time": "2025-02-26T09:16:37+00:00"
+ },
+ {
+ "name": "orchestra/testbench",
+ "version": "v10.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/testbench.git",
+ "reference": "b10f1dec6253a87961349e92ac1420c9aebfacd0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/testbench/zipball/b10f1dec6253a87961349e92ac1420c9aebfacd0",
+ "reference": "b10f1dec6253a87961349e92ac1420c9aebfacd0",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "fakerphp/faker": "^1.23",
+ "laravel/framework": "^12.0.1",
+ "mockery/mockery": "^1.6.10",
+ "orchestra/testbench-core": "^10.0.0",
+ "orchestra/workbench": "^10.0.0",
+ "php": "^8.2",
+ "phpunit/phpunit": "^11.5.3",
+ "symfony/process": "^7.2",
+ "symfony/yaml": "^7.2",
+ "vlucas/phpdotenv": "^5.6.1"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com",
+ "homepage": "https://github.com/crynobone"
+ }
+ ],
+ "description": "Laravel Testing Helper for Packages Development",
+ "homepage": "https://packages.tools/testbench/",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/testbench/issues",
+ "source": "https://github.com/orchestral/testbench/tree/v10.0.0"
+ },
+ "time": "2025-02-24T13:49:40+00:00"
+ },
+ {
+ "name": "orchestra/testbench-core",
+ "version": "v10.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/testbench-core.git",
+ "reference": "4a412b377ab8c616fc4c239f50bf82e4092b859b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/4a412b377ab8c616fc4c239f50bf82e4092b859b",
+ "reference": "4a412b377ab8c616fc4c239f50bf82e4092b859b",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "orchestra/sidekick": "^1.0.3",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.31",
+ "symfony/polyfill-php84": "^1.31"
+ },
+ "conflict": {
+ "brianium/paratest": "<7.3.0 || >=8.0.0",
+ "laravel/framework": "<12.0.0 || >=13.0.0",
+ "laravel/serializable-closure": "<1.3.0 || >=3.0.0",
+ "nunomaduro/collision": "<8.0.0 || >=9.0.0",
+ "phpunit/phpunit": "<10.5.35 || >=11.0.0 <11.5.3 || 12.0.0 || >=12.1.0"
+ },
+ "require-dev": {
+ "fakerphp/faker": "^1.24",
+ "laravel/framework": "^12.0.0",
+ "laravel/pint": "^1.21",
+ "laravel/serializable-closure": "^1.3 || ^2.0",
+ "mockery/mockery": "^1.6.10",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^10.5.35 || ^11.5.3 || ^12.0.1",
+ "spatie/laravel-ray": "^1.39.1",
+ "symfony/process": "^7.2.0",
+ "symfony/yaml": "^7.2.0",
+ "vlucas/phpdotenv": "^5.6.1"
+ },
+ "suggest": {
+ "brianium/paratest": "Allow using parallel testing (^7.3).",
+ "ext-pcntl": "Required to use all features of the console signal trapping.",
+ "fakerphp/faker": "Allow using Faker for testing (^1.23).",
+ "laravel/framework": "Required for testing (^12.0.0).",
+ "mockery/mockery": "Allow using Mockery for testing (^1.6).",
+ "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^8.0).",
+ "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^9.0).",
+ "phpunit/phpunit": "Allow using PHPUnit for testing (^10.5 || ^11.0 || ^12.0).",
+ "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^7.2).",
+ "symfony/yaml": "Required for Testbench CLI (^7.2).",
+ "vlucas/phpdotenv": "Required for Testbench CLI (^5.4.1)."
+ },
+ "bin": [
+ "testbench"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Orchestra\\Testbench\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com",
+ "homepage": "https://github.com/crynobone"
+ }
+ ],
+ "description": "Testing Helper for Laravel Development",
+ "homepage": "https://packages.tools/testbench",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/testbench/issues",
+ "source": "https://github.com/orchestral/testbench-core"
+ },
+ "time": "2025-02-25T09:32:21+00:00"
+ },
+ {
+ "name": "orchestra/workbench",
+ "version": "v10.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/workbench.git",
+ "reference": "93fcc3ec455a12e7468cacbab28179968ac68468"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/workbench/zipball/93fcc3ec455a12e7468cacbab28179968ac68468",
+ "reference": "93fcc3ec455a12e7468cacbab28179968ac68468",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "fakerphp/faker": "^1.23",
+ "laravel/framework": "^12.0.0",
+ "laravel/pail": "^1.2.2",
+ "laravel/tinker": "^2.10.1",
+ "nunomaduro/collision": "^8.6",
+ "orchestra/canvas": "^10.0.1",
+ "orchestra/sidekick": "^1.0.3",
+ "orchestra/testbench-core": "~10.0.0",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.31",
+ "symfony/polyfill-php84": "^1.31",
+ "symfony/process": "^7.2",
+ "symfony/yaml": "^7.2"
+ },
+ "require-dev": {
+ "laravel/pint": "^1.21",
+ "mockery/mockery": "^1.6.10",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^11.5.3",
+ "spatie/laravel-ray": "^1.39.1"
+ },
+ "suggest": {
+ "ext-pcntl": "Required to use all features of the console signal trapping."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Workbench\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Workbench Companion for Laravel Packages Development",
+ "keywords": [
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/workbench/issues",
+ "source": "https://github.com/orchestral/workbench/tree/v10.0.0"
+ },
+ "time": "2025-02-24T13:39:13+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpmyadmin/sql-parser",
+ "version": "5.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpmyadmin/sql-parser.git",
+ "reference": "07044bc8c13abd542756c3fd34dc66a5d6dee8e4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/07044bc8c13abd542756c3fd34dc66a5d6dee8e4",
+ "reference": "07044bc8c13abd542756c3fd34dc66a5d6dee8e4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "phpmyadmin/motranslator": "<3.0"
+ },
+ "require-dev": {
+ "phpbench/phpbench": "^1.1",
+ "phpmyadmin/coding-standard": "^3.0",
+ "phpmyadmin/motranslator": "^4.0 || ^5.0",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^1.12",
+ "phpstan/phpstan-deprecation-rules": "^1.2",
+ "phpstan/phpstan-phpunit": "^1.4",
+ "phpstan/phpstan-strict-rules": "^1.6",
+ "phpunit/phpunit": "^8.5 || ^9.6",
+ "psalm/plugin-phpunit": "^0.16.1",
+ "vimeo/psalm": "^4.11",
+ "zumba/json-serializer": "~3.0.2"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance",
+ "phpmyadmin/motranslator": "Translate messages to your favorite locale"
+ },
+ "bin": [
+ "bin/highlight-query",
+ "bin/lint-query",
+ "bin/sql-parser",
+ "bin/tokenize-query"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpMyAdmin\\SqlParser\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "The phpMyAdmin Team",
+ "email": "developers@phpmyadmin.net",
+ "homepage": "https://www.phpmyadmin.net/team/"
+ }
+ ],
+ "description": "A validating SQL lexer and parser with a focus on MySQL dialect.",
+ "homepage": "https://github.com/phpmyadmin/sql-parser",
+ "keywords": [
+ "analysis",
+ "lexer",
+ "parser",
+ "query linter",
+ "sql",
+ "sql lexer",
+ "sql linter",
+ "sql parser",
+ "sql syntax highlighter",
+ "sql tokenizer"
+ ],
+ "support": {
+ "issues": "https://github.com/phpmyadmin/sql-parser/issues",
+ "source": "https://github.com/phpmyadmin/sql-parser"
+ },
+ "funding": [
+ {
+ "url": "https://www.phpmyadmin.net/donate/",
+ "type": "other"
+ }
+ ],
+ "time": "2025-02-22T20:00:59+00:00"
+ },
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:41:07+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c",
+ "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-19T15:46:42+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7",
+ "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.4.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-text-template": "^4.0.1",
+ "sebastian/code-unit-reverse-lookup": "^4.0.1",
+ "sebastian/complexity": "^4.0.1",
+ "sebastian/environment": "^7.2.0",
+ "sebastian/lines-of-code": "^3.0.1",
+ "sebastian/version": "^5.0.2",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.2"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-25T13:26:39+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-27T05:02:59+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:07:44+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:08:43+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:09:35+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.5.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "d5df2b32d729562ff8db634678d71085ee579006"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d5df2b32d729562ff8db634678d71085ee579006",
+ "reference": "d5df2b32d729562ff8db634678d71085ee579006",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.0",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0.8",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-invoker": "^5.0.1",
+ "phpunit/php-text-template": "^4.0.1",
+ "phpunit/php-timer": "^7.0.1",
+ "sebastian/cli-parser": "^3.0.2",
+ "sebastian/code-unit": "^3.0.2",
+ "sebastian/comparator": "^6.3.0",
+ "sebastian/diff": "^6.0.2",
+ "sebastian/environment": "^7.2.0",
+ "sebastian/exporter": "^6.3.0",
+ "sebastian/global-state": "^7.0.2",
+ "sebastian/object-enumerator": "^6.0.1",
+ "sebastian/type": "^5.1.0",
+ "sebastian/version": "^5.0.2",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.10"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-25T06:11:48+00:00"
+ },
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "psy/psysh",
+ "version": "v0.12.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bobthecow/psysh.git",
+ "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c",
+ "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "nikic/php-parser": "^5.0 || ^4.0",
+ "php": "^8.0 || ^7.4",
+ "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
+ "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
+ },
+ "conflict": {
+ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2"
+ },
+ "suggest": {
+ "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+ "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+ "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well."
+ },
+ "bin": [
+ "bin/psysh"
+ ],
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": false,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-main": "0.12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Psy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com/"
+ }
+ ],
+ "description": "An interactive shell for modern PHP.",
+ "homepage": "http://psysh.org/",
+ "keywords": [
+ "REPL",
+ "console",
+ "interactive",
+ "shell"
+ ],
+ "support": {
+ "issues": "https://github.com/bobthecow/psysh/issues",
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.7"
+ },
+ "time": "2024-12-10T01:58:33+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:41:36+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca",
+ "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-12T09:59:06+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:45:54+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "d4e47a769525c4dd38cea90e5dcd435ddbbc7115"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/d4e47a769525c4dd38cea90e5dcd435ddbbc7115",
+ "reference": "d4e47a769525c4dd38cea90e5dcd435ddbbc7115",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.4"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-06T10:28:19+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:49:50+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5",
+ "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:54:44+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3",
+ "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-05T09:17:50+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:57:36+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:58:38+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:00:13+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:01:32+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "694d156164372abbd149a4b85ccda2e4670c0e16"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16",
+ "reference": "694d156164372abbd149a4b85ccda2e4670c0e16",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:10:34+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac",
+ "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-09-17T13:12:04+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T05:16:32+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "symfony/clock",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24",
+ "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "clock",
+ "psr20",
+ "time"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/clock/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
+ "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-11T03:49:26+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/error-handler",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "aabf79938aa795350c07ce6464dd1985607d95d5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5",
+ "reference": "aabf79938aa795350c07ce6464dd1985607d95d5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/deprecation-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4"
+ },
+ "require-dev": {
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/serializer": "^6.4|^7.0"
+ },
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-02T20:27:07+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1",
+ "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f",
+ "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v7.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "87a71856f2f56e4100373e92529eed3171695cfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb",
+ "reference": "87a71856f2f56e4100373e92529eed3171695cfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v7.2.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-30T19:00:17+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v7.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0",
+ "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php83": "^1.27"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.6",
+ "symfony/cache": "<6.4.12|>=7.0,<7.1.5"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6|^4",
+ "predis/predis": "^1.1|^2.0",
+ "symfony/cache": "^6.4.12|^7.1.5",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/mime": "^6.4|^7.0",
+ "symfony/rate-limiter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v7.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-17T10:56:55+00:00"
+ },
+ {
+ "name": "symfony/http-kernel",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "9f1103734c5789798fefb90e91de4586039003ed"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed",
+ "reference": "9f1103734c5789798fefb90e91de4586039003ed",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/cache": "<6.4",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/doctrine-bridge": "<6.4",
+ "symfony/form": "<6.4",
+ "symfony/http-client": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/mailer": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/translation": "<6.4",
+ "symfony/translation-contracts": "<2.5",
+ "symfony/twig-bridge": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/var-dumper": "<6.4",
+ "twig/twig": "<3.12"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^6.4|^7.0",
+ "symfony/clock": "^6.4|^7.0",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/css-selector": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/dom-crawler": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/finder": "^6.4|^7.0",
+ "symfony/http-client-contracts": "^2.5|^3",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/property-access": "^7.1",
+ "symfony/routing": "^6.4|^7.0",
+ "symfony/serializer": "^7.1",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/translation": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/uid": "^6.4|^7.0",
+ "symfony/validator": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0",
+ "symfony/var-exporter": "^6.4|^7.0",
+ "twig/twig": "^3.12"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-26T11:01:22+00:00"
+ },
+ {
+ "name": "symfony/mailer",
+ "version": "v7.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3",
+ "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/mime": "^7.2",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/twig-bridge": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps sending emails",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/mailer/tree/v7.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-27T11:08:17+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "87ca22046b78c3feaff04b337f33b38510fd686b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b",
+ "reference": "87ca22046b78c3feaff04b337f33b38510fd686b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/property-access": "^6.4|^7.0",
+ "symfony/property-info": "^6.4|^7.0",
+ "symfony/serializer": "^6.4.3|^7.0.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-19T08:51:20+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
+ "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2",
+ "symfony/polyfill-intl-normalizer": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php83",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491",
+ "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php83\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "e5493eb51311ab0b1cc2243416613f06ed8f18bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/e5493eb51311ab0b1cc2243416613f06ed8f18bd",
+ "reference": "e5493eb51311ab0b1cc2243416613f06ed8f18bd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T12:04:04+00:00"
+ },
+ {
+ "name": "symfony/polyfill-uuid",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-uuid.git",
+ "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
+ "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-uuid": "*"
+ },
+ "suggest": {
+ "ext-uuid": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Uuid\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for uuid functions",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
+ "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-05T08:33:46+00:00"
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v7.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996",
+ "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/yaml": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Maps an HTTP request to a set of configuration variables",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v7.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-17T10:56:55+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82",
+ "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-13T13:31:26+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "283856e6981286cc0d800b53bd5703e8e363f05a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a",
+ "reference": "283856e6981286cc0d800b53bd5703e8e363f05a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.5|^3.0"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/service-contracts": "<2.5",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3|3.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.18|^5.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/finder": "^6.4|^7.0",
+ "symfony/http-client-contracts": "^2.5|^3.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/routing": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-13T10:27:23+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
+ "reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "symfony/uid",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/uid.git",
+ "reference": "2d294d0c48df244c71c105a169d0190bfb080426"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426",
+ "reference": "2d294d0c48df244c71c105a169d0190bfb080426",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-uuid": "^1.15"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Uid\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to generate and represent UIDs",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "UID",
+ "ulid",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/uid/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v7.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "82b478c69745d8878eb60f9a049a4d584996f73a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a",
+ "reference": "82b478c69745d8878eb60f9a049a4d584996f73a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/uid": "^6.4|^7.0",
+ "twig/twig": "^3.12"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com/",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v7.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-17T11:39:41+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v7.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec",
+ "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0"
+ },
+ "bin": [
+ "Resources/bin/yaml-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Loads and dumps YAML files",
+ "homepage": "https://symfony.com/",
+ "support": {
+ "source": "https://github.com/symfony/yaml/tree/v7.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-07T12:55:42+00:00"
+ },
+ {
+ "name": "thecodingmachine/phpstan-safe-rule",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thecodingmachine/phpstan-safe-rule.git",
+ "reference": "33dcbc3228c55ea4c364ecf74a3661cf7b7f168d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thecodingmachine/phpstan-safe-rule/zipball/33dcbc3228c55ea4c364ecf74a3661cf7b7f168d",
+ "reference": "33dcbc3228c55ea4c364ecf74a3661cf7b7f168d",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5",
+ "php": "^8.1",
+ "phpstan/phpstan": "^2.0",
+ "thecodingmachine/safe": "^1.2 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^10.4",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "phpstan-safe-rule.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "TheCodingMachine\\Safe\\PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "David Négrier",
+ "email": "d.negrier@thecodingmachine.com"
+ }
+ ],
+ "description": "A PHPStan rule to detect safety issues. Must be used in conjunction with thecodingmachine/safe",
+ "support": {
+ "issues": "https://github.com/thecodingmachine/phpstan-safe-rule/issues",
+ "source": "https://github.com/thecodingmachine/phpstan-safe-rule/tree/v1.4.0"
+ },
+ "time": "2025-02-11T12:41:29+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:36:25+00:00"
+ },
+ {
+ "name": "tijsverkoyen/css-to-inline-styles",
+ "version": "v2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
+ "reference": "0d72ac1c00084279c1816675284073c5a337c20d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d",
+ "reference": "0d72ac1c00084279c1816675284073c5a337c20d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": "^7.4 || ^8.0",
+ "symfony/css-selector": "^5.4 || ^6.0 || ^7.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^8.5.21 || ^9.5.10"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "TijsVerkoyen\\CssToInlineStyles\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Tijs Verkoyen",
+ "email": "css_to_inline_styles@verkoyen.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
+ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+ "support": {
+ "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+ "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0"
+ },
+ "time": "2024-12-21T16:25:41+00:00"
+ },
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v5.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.1.3",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3",
+ "symfony/polyfill-ctype": "^1.24",
+ "symfony/polyfill-mbstring": "^1.24",
+ "symfony/polyfill-php80": "^1.24"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-filter": "*",
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "5.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "https://github.com/vlucas"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:52:34+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
+ "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-21T01:49:47+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ },
+ "time": "2022-06-03T18:03:27+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {},
+ "prefer-stable": true,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "^8.1",
+ "ext-json": "*"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
diff --git a/config/cloud-tasks.php b/config/cloud-tasks.php
new file mode 100644
index 0000000..5a9b3a5
--- /dev/null
+++ b/config/cloud-tasks.php
@@ -0,0 +1,16 @@
+ env('CLOUD_TASKS_URI', 'handle-task'),
+
+ // If the application only dispatches jobs
+ 'disable_task_handler' => env('CLOUD_TASKS_DISABLE_TASK_HANDLER', false),
+
+ // Optionally, pass custom options to the Cloud Tasks API client
+ 'client_options' => [
+ // 'credentials' => '/path/to/custom/credentials.json',
+ ],
+];
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..924280e
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,27 @@
+services:
+ app:
+ build:
+ context: .
+ dockerfile: app.Dockerfile
+ env_file:
+ - .env
+ volumes:
+ - .:/var/www/html
+ - ${CI_SERVICE_ACCOUNT_JSON_KEY_PATH-./tests/Support/gcloud-key-valid.json}:/var/www/html/tests/Support/gcloud-key-valid.json
+ mysql:
+ image: 'mysql:${MYSQL_VERSION:-8.0}'
+ ports:
+ - '${MYSQL_PORT:-3307}:3306'
+ environment:
+ MYSQL_USER: 'cloudtasks'
+ MYSQL_PASSWORD: 'cloudtasks'
+ MYSQL_DATABASE: 'cloudtasks'
+ MYSQL_ROOT_PASSWORD: 'root'
+ pgsql:
+ image: 'postgres:${PGSQL_VERSION:-14}'
+ ports:
+ - '${POSTGRES_PORT:-5432}:5432'
+ environment:
+ POSTGRES_USER: 'cloudtasks'
+ POSTGRES_PASSWORD: 'cloudtasks'
+ POSTGRES_DB: 'cloudtasks'
diff --git a/logo.png b/logo.png
deleted file mode 100644
index f887ad2..0000000
Binary files a/logo.png and /dev/null differ
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..579f511
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,9 @@
+includes:
+ - ./vendor/larastan/larastan/extension.neon
+ - ./vendor/thecodingmachine/phpstan-safe-rule/phpstan-safe-rule.neon
+parameters:
+ paths:
+ - src
+ level: 9
+ ignoreErrors:
+ - "/dispatchAfterCommit with no type specified/"
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index 8d3ba30..26d0014 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,32 +1,24 @@
-
-
-
-
- ./tests/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- src/
-
-
+
+
+
+ ./tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./src
+
+
diff --git a/pint.json b/pint.json
new file mode 100644
index 0000000..ee12b92
--- /dev/null
+++ b/pint.json
@@ -0,0 +1,10 @@
+{
+ "preset": "laravel",
+ "rules": {
+ "fully_qualified_strict_types": true,
+ "declare_strict_types": true,
+ "ordered_imports": {
+ "sort_algorithm": "length"
+ }
+ }
+}
diff --git a/src/CloudTasksApi.php b/src/CloudTasksApi.php
new file mode 100644
index 0000000..0b961fa
--- /dev/null
+++ b/src/CloudTasksApi.php
@@ -0,0 +1,27 @@
+client->createTask(new CreateTaskRequest([
+ 'parent' => $queueName,
+ 'task' => $task,
+ ]));
+ }
+
+ /**
+ * @throws ApiException
+ */
+ public function deleteTask(string $taskName): void
+ {
+ $this->client->deleteTask(new DeleteTaskRequest([
+ 'name' => $taskName,
+ ]));
+ }
+
+ /**
+ * @throws ApiException
+ */
+ public function getTask(string $taskName): Task
+ {
+ return $this->client->getTask(new GetTaskRequest([
+ 'name' => $taskName,
+ ]));
+ }
+
+ public function exists(string $taskName): bool
+ {
+ try {
+ $this->getTask($taskName);
+
+ return true;
+ } catch (ApiException $e) {
+ if ($e->getStatus() === 'NOT_FOUND') {
+ return false;
+ }
+
+ report($e);
+ }
+
+ return false;
+ }
+}
diff --git a/src/CloudTasksApiContract.php b/src/CloudTasksApiContract.php
new file mode 100644
index 0000000..5f0af35
--- /dev/null
+++ b/src/CloudTasksApiContract.php
@@ -0,0 +1,18 @@
+
+ */
+ public array $createdTasks = [];
+
+ /**
+ * @var array
+ */
+ public array $deletedTasks = [];
+
+ public function createTask(string $queueName, Task $task): Task
+ {
+ $this->createdTasks[] = compact('queueName', 'task');
+
+ return $task;
+ }
+
+ public function deleteTask(string $taskName): void
+ {
+ $this->deletedTasks[] = $taskName;
+ }
+
+ public function getTask(string $taskName): Task
+ {
+ return (new Task)->setName($taskName);
+ }
+
+ public function exists(string $taskName): bool
+ {
+ foreach ($this->createdTasks as $createdTask) {
+ if ($createdTask['task']->getName() === $taskName) {
+ return ! in_array($taskName, $this->deletedTasks);
+ }
+ }
+
+ return false;
+ }
+
+ public function assertTaskDeleted(string $taskName): void
+ {
+ Assert::assertTrue(
+ in_array($taskName, $this->deletedTasks),
+ 'The task ['.$taskName.'] should have been deleted but it is not.'
+ );
+ }
+
+ public function assertTaskNotDeleted(string $taskName): void
+ {
+ Assert::assertTrue(
+ ! in_array($taskName, $this->deletedTasks),
+ 'The task ['.$taskName.'] should not have been deleted but it was.'
+ );
+ }
+
+ public function assertDeletedTaskCount(int $count): void
+ {
+ Assert::assertCount($count, $this->deletedTasks);
+ }
+
+ public function assertTaskCreated(Closure $closure): void
+ {
+ $count = count(array_filter($this->createdTasks, function ($createdTask) use ($closure) {
+ return $closure($createdTask['task'], $createdTask['queueName']);
+ }));
+
+ Assert::assertTrue($count > 0, 'Task was not created.');
+ }
+
+ public function assertCreatedTaskCount(int $count): void
+ {
+ Assert::assertCount($count, $this->createdTasks);
+ }
+}
diff --git a/src/CloudTasksConnector.php b/src/CloudTasksConnector.php
index a9be76f..9ce5ae1 100644
--- a/src/CloudTasksConnector.php
+++ b/src/CloudTasksConnector.php
@@ -1,16 +1,37 @@
container = $container;
+ $this->driver = $driver;
$this->job = $job;
- $this->attempts = $attempts;
- $this->container = Container::getInstance();
+ $this->connectionName = $connectionName;
+ $this->queue = $queue;
}
- public function getJobId()
+ public function getJobId(): string
{
- return $this->job['uuid'];
+ return $this->uuid() ?? throw new Exception;
}
- public function getRawBody()
+ /**
+ * @throws JsonException
+ */
+ public function getRawBody(): string
{
return json_encode($this->job);
}
- public function attempts()
+ public function attempts(): int
+ {
+ return $this->job['internal']['attempts'] ?? 0;
+ }
+
+ public function setAttempts(int $attempts): void
{
- return $this->attempts;
+ $this->job['internal']['attempts'] = $attempts;
+ }
+
+ public function delete(): void
+ {
+ // Laravel automatically calls delete() after a job is processed successfully.
+ // However, this is not what we want to happen in Cloud Tasks because Cloud Tasks
+ // will also delete the task upon a 200 OK status, which means a task is deleted twice.
+ }
+
+ public function release($delay = 0): void
+ {
+ parent::release($delay);
+
+ $this->driver->release($this, $delay);
+
+ if (! data_get($this->job, 'internal.errored')) {
+ event(new JobReleased($this->getConnectionName(), $this, $delay));
+ }
}
}
diff --git a/src/CloudTasksQueue.php b/src/CloudTasksQueue.php
index 13901f3..6c01dbb 100644
--- a/src/CloudTasksQueue.php
+++ b/src/CloudTasksQueue.php
@@ -1,103 +1,364 @@
client = $client;
- $this->default = $config['queue'];
+ self::$workerOptionsCallback = null;
}
- public function size($queue = null)
+ /**
+ * Get the size of the queue.
+ *
+ * @param string|null $queue
+ */
+ public function size($queue = null): int
{
- // TODO: Implement size() method.
+ // It is not possible to know the number of tasks in the queue.
+ return 0;
}
+ /**
+ * Push a new job onto the queue.
+ *
+ * @param string|Closure|JobBeforeDispatch $job
+ * @param mixed $data
+ * @param string|null $queue
+ * @return mixed
+ */
public function push($job, $data = '', $queue = null)
{
- return $this->pushToCloudTasks($queue, $this->createPayload(
- $job, $this->getQueue($queue), $data
- ));
+ if (! $queue) {
+ $queue = $this->getQueueForJob($job);
+ }
+
+ if (is_object($job) && ! $job instanceof Closure) {
+ /** @var JobBeforeDispatch $job */
+ $job->queue = $queue;
+ }
+
+ return $this->enqueueUsing(
+ $job,
+ $this->createPayload($job, $queue, $data),
+ $queue,
+ null,
+ function ($payload, $queue) use ($job) {
+ return $this->pushRaw($payload, $queue, ['job' => $job]);
+ }
+ );
}
+ /**
+ * Push a raw payload onto the queue.
+ *
+ * @param string $payload
+ * @param string|null $queue
+ * @param JobOptions $options
+ * @return string
+ */
public function pushRaw($payload, $queue = null, array $options = [])
{
- return $this->pushToCloudTasks($queue, $payload);
+ $delay = ! empty($options['delay']) ? $options['delay'] : 0;
+ $job = $options['job'] ?? null;
+
+ return $this->pushToCloudTasks($queue, $payload, $delay, $job);
}
+ /**
+ * Push a new job onto the queue after a delay.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @param Closure|string|JobBeforeDispatch $job
+ * @param mixed $data
+ * @param string|null $queue
+ * @return mixed
+ */
public function later($delay, $job, $data = '', $queue = null)
{
- return $this->pushToCloudTasks($queue, $this->createPayload(
- $job, $this->getQueue($queue), $data
- ), $delay);
+ // Laravel pls fix your typehints
+ if (! $queue) {
+ $queue = $this->getQueueForJob($job);
+ }
+
+ return $this->enqueueUsing(
+ $job,
+ $this->createPayload($job, $queue, $data),
+ $queue,
+ $delay,
+ function ($payload, $queue, $delay) use ($job) {
+ return $this->pushToCloudTasks($queue, $payload, $delay, $job);
+ }
+ );
}
- protected function pushToCloudTasks($queue, $payload, $delay = 0, $attempts = 0)
+ /**
+ * Push a job to Cloud Tasks.
+ *
+ * @param string|null $queue
+ * @param string $payload
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @param Closure|string|object|null $job
+ * @return string
+ */
+ protected function pushToCloudTasks($queue, $payload, $delay, mixed $job)
{
- $queue = $this->getQueue($queue);
- $queueName = $this->client->queueName(Config::project(), Config::location(), $queue);
- $availableAt = $this->availableAt($delay);
+ $queue = $queue ?: $this->config['queue'];
+
+ $payload = (array) json_decode($payload, true);
- $httpRequest = $this->createHttpRequest();
- $httpRequest->setUrl(Config::handler());
- $httpRequest->setHttpMethod(HttpMethod::POST);
- $httpRequest->setBody($payload);
+ /** @var JobShape $payload */
+ $task = tap(new Task)->setName($this->taskName($queue, $payload['displayName']));
- $task = $this->createTask();
- $task->setHttpRequest($httpRequest);
+ $payload = $this->enrichPayloadWithAttempts($payload);
- $token = new OidcToken;
- $token->setServiceAccountEmail(Config::serviceAccountEmail());
- $httpRequest->setOidcToken($token);
+ $this->addPayloadToTask($payload, $task, $job);
+ $availableAt = $this->availableAt($delay);
if ($availableAt > time()) {
$task->setScheduleTime(new Timestamp(['seconds' => $availableAt]));
}
- $this->client->createTask($queueName, $task);
+ $queueName = $this->client->queueName($this->config['project'], $this->config['location'], $queue);
+ CloudTasksApi::createTask($queueName, $task);
+
+ event(new TaskCreated($queue, $task));
+
+ return $payload['uuid'];
+ }
+
+ private function taskName(string $queueName, string $displayName): string
+ {
+ return CloudTasksClient::taskName(
+ $this->config['project'],
+ $this->config['location'],
+ $queueName,
+ str($displayName)
+ ->afterLast('\\')
+ ->replaceMatches('![^-\pL\pN\s]+!u', '-')
+ ->replaceMatches('![-\s]+!u', '-')
+ ->prepend((string) Str::ulid(), '-')
+ ->toString(),
+ );
+ }
+
+ /**
+ * @param JobShape $payload
+ * @return JobShape
+ */
+ private function enrichPayloadWithAttempts(array $payload): array
+ {
+ $payload['internal'] = [
+ 'attempts' => $payload['internal']['attempts'] ?? 0,
+ ];
+
+ return $payload;
+ }
+
+ /**
+ * @param Closure|string|object|null $job
+ * @param JobShape $payload
+ */
+ public function addPayloadToTask(array $payload, Task $task, $job): Task
+ {
+ $headers = $this->headers($payload);
+
+ if (! empty($this->config['app_engine'])) {
+ $path = \Safe\parse_url(http://github.com/route('cloud-tasks.handle-task'), PHP_URL_PATH);
+
+ if (! is_string($path)) {
+ throw new Exception('Something went wrong parsing the route.');
+ }
+
+ $appEngineRequest = new AppEngineHttpRequest;
+ $appEngineRequest->setRelativeUri($path);
+ $appEngineRequest->setHttpMethod(HttpMethod::POST);
+ $appEngineRequest->setBody(json_encode($payload));
+ $appEngineRequest->setHeaders($headers);
+
+ if (! empty($this->config['app_engine_service'])) {
+ $routing = new AppEngineRouting;
+ $routing->setService($this->config['app_engine_service']);
+ $appEngineRequest->setAppEngineRouting($routing);
+ }
+
+ $task->setAppEngineHttpRequest($appEngineRequest);
+ } else {
+ $httpRequest = new HttpRequest;
+ $httpRequest->setUrl($this->getHandler($job));
+ $httpRequest->setBody(json_encode($payload));
+ $httpRequest->setHttpMethod(HttpMethod::POST);
+ $httpRequest->setHeaders($headers);
+
+ $token = new OidcToken;
+ $token->setServiceAccountEmail($this->config['service_account_email'] ?? '');
+ $httpRequest->setOidcToken($token);
+ $task->setHttpRequest($httpRequest);
+
+ if (! empty($this->config['dispatch_deadline'])) {
+ $task->setDispatchDeadline((new Duration)->setSeconds($this->config['dispatch_deadline']));
+ }
+ }
+
+ return $task;
}
public function pop($queue = null)
{
- // TODO: Implement pop() method.
+ // It is not possible to pop a job from the queue.
+ return null;
+ }
+
+ public function delete(CloudTasksJob $job): void
+ {
+ // Job deletion will be handled by Cloud Tasks.
+ }
+
+ public function release(CloudTasksJob $job, int $delay = 0): void
+ {
+ $this->pushRaw(
+ payload: $job->getRawBody(),
+ queue: $job->getQueue(),
+ options: ['delay' => $delay, 'job' => $job],
+ );
}
- private function getQueue($queue = null)
+ /**
+ * @param Closure|string|object|null $job
+ */
+ public function getHandler(mixed $job): string
{
- return $queue ?: $this->default;
+ if (static::$handlerUrlCallback) {
+ return (static::$handlerUrlCallback)($job);
+ }
+
+ if (empty($this->config['handler'])) {
+ $this->config['handler'] = request()->getSchemeAndHttpHost();
+ }
+
+ $handler = rtrim($this->config['handler'], '/');
+
+ if (str_ends_with($handler, '/'.config()->string('cloud-tasks.uri'))) {
+ return $handler;
+ }
+
+ return $handler.'/'.config()->string('cloud-tasks.uri');
}
/**
- * @return HttpRequest
+ * @param array $payload
+ * @return array
*/
- private function createHttpRequest()
+ private function headers(mixed $payload): array
{
- return app(HttpRequest::class);
+ if (! static::$taskHeadersCallback) {
+ return [];
+ }
+
+ return (static::$taskHeadersCallback)($payload);
}
/**
- * @return Task
+ * @param Closure|string|JobBeforeDispatch $job
*/
- private function createTask()
+ private function getQueueForJob(mixed $job): string
{
- return app(Task::class);
+ if (is_object($job) && ! $job instanceof Closure) {
+ /** @var JobBeforeDispatch $job */
+ if (! empty($job->queue)) {
+ return $job->queue;
+ }
+ }
+
+ return $this->config['queue'];
}
}
diff --git a/src/CloudTasksServiceProvider.php b/src/CloudTasksServiceProvider.php
index 14f9905..1301f57 100644
--- a/src/CloudTasksServiceProvider.php
+++ b/src/CloudTasksServiceProvider.php
@@ -1,39 +1,115 @@
registerClient();
- $this->registerConnector($queue);
- $this->registerRoutes($router);
+ $this->registerConnector();
+ $this->registerConfig();
+ $this->registerRoutes();
+ $this->registerEvents();
}
- private function registerClient()
+ private function registerClient(): void
{
$this->app->singleton(CloudTasksClient::class, function () {
- return new CloudTasksClient([
- 'credentials' => Config::credentials(),
- ]);
+ return new CloudTasksClient(config()->array('cloud-tasks.client_options', []));
});
+
+ $this->app->singleton('cloud-tasks.worker', function (Application $app) {
+ return new Worker(
+ $app['queue'],
+ $app['events'],
+ $app[ExceptionHandler::class],
+ fn () => $app->isDownForMaintenance(),
+ );
+ });
+
+ $this->app->bind('cloud-tasks-api', CloudTasksApiConcrete::class);
}
- private function registerConnector(QueueManager $queue)
+ private function registerConnector(): void
{
- $queue->addConnector('cloudtasks', function () {
- return new CloudTasksConnector;
+ with(resolve('queue'), function (QueueManager $queue) {
+ $queue->addConnector('cloudtasks', function () {
+ return new CloudTasksConnector;
+ });
});
}
- private function registerRoutes(Router $router)
+ private function registerConfig(): void
{
- $router->post('handle-task', [TaskHandler::class, 'handle']);
+ $this->publishes([
+ __DIR__.'/../config/cloud-tasks.php' => config_path('cloud-tasks.php'),
+ ], ['cloud-tasks']);
+
+ $this->mergeConfigFrom(__DIR__.'/../config/cloud-tasks.php', 'cloud-tasks');
+ }
+
+ private function registerRoutes(): void
+ {
+ if (config('cloud-tasks.disable_task_handler')) {
+ return;
+ }
+
+ with(resolve('router'), function (Router $router) {
+ $router->post(config()->string('cloud-tasks.uri'), [TaskHandler::class, 'handle'])
+ ->name('cloud-tasks.handle-task');
+ });
+ }
+
+ private function registerEvents(): void
+ {
+ /** @var Dispatcher $events */
+ $events = app('events');
+
+ $events->listen(JobFailed::class, function (JobFailed $event) {
+ if (! $event->job instanceof CloudTasksJob) {
+ return;
+ }
+
+ app('queue.failer')->log(
+ $event->job->getConnectionName(),
+ $event->job->getQueue(),
+ $event->job->getRawBody(),
+ $event->exception,
+ );
+ });
+
+ $events->listen(JobExceptionOccurred::class, function (JobExceptionOccurred $event) {
+ if (! $event->job instanceof CloudTasksJob) {
+ return;
+ }
+
+ $event->job->job['internal']['errored'] = true;
+ });
+
+ $events->listen(JobFailed::class, function ($event) {
+ if (! $event->job instanceof CloudTasksJob) {
+ return;
+ }
+ });
+
+ $events->listen(JobReleased::class, function (JobReleased $event) {
+ if (! $event->job instanceof CloudTasksJob) {
+ return;
+ }
+ });
}
}
diff --git a/src/Config.php b/src/Config.php
deleted file mode 100644
index c41713f..0000000
--- a/src/Config.php
+++ /dev/null
@@ -1,60 +0,0 @@
-command();
+
+ return $command['connection']
+ ?? config()->string('queue.default');
+ }
+
+ public function queue(): string
+ {
+ $command = $this->command();
+
+ return $command['queue']
+ ?? config()->string('queue.connections.'.$this->connection().'.queue');
+ }
+
+ public function shortTaskName(): string
+ {
+ return request()->header('X-CloudTasks-TaskName')
+ ?? request()->header('X-AppEngine-TaskName')
+ ?? throw new Error('Unable to extract taskname from header');
+ }
+
+ public function fullyQualifiedTaskName(): string
+ {
+ /** @var QueueConfig $config */
+ $config = config('queue.connections.'.$this->connection());
+
+ return CloudTasksClient::taskName(
+ project: $config['project'],
+ location: $config['location'],
+ queue: $this->queue(),
+ task: $this->shortTaskName(),
+ );
+ }
+
+ /**
+ * @return JobCommand
+ */
+ public function command(): array
+ {
+ $command = $this->task['data']['command'];
+
+ if (str_starts_with($command, 'O:')) {
+ // @phpstan-ignore-next-line
+ return (array) unserialize($command, ['allowed_classes' => false]);
+ }
+
+ if (app()->bound(Encrypter::class)) {
+ // @phpstan-ignore-next-line
+ return (array) unserialize(app(Encrypter::class)->decrypt($command));
+ }
+
+ return [];
+ }
+
+ /**
+ * @return JobShape
+ */
+ public function toArray(): array
+ {
+ return $this->task;
+ }
+}
diff --git a/src/OpenIdVerificator.php b/src/OpenIdVerificator.php
deleted file mode 100644
index 0a818bb..0000000
--- a/src/OpenIdVerificator.php
+++ /dev/null
@@ -1,73 +0,0 @@
-guzzle = $guzzle;
- $this->rsa = $rsa;
- }
-
- public function getPublicKey($kid = null)
- {
- $v3Certs = Cache::rememberForever(self::V3_CERTS, function () {
- return $this->getv3Certs();
- });
-
- $cert = $kid ? collect($v3Certs)->firstWhere('kid', '=', $kid) : $v3Certs[0];
-
- return $this->extractPublicKeyFromCertificate($cert);
- }
-
- private function getv3Certs()
- {
- $jwksUri = $this->callApiAndReturnValue(self::URL_OPENID_CONFIG, 'jwks_uri');
-
- return $this->callApiAndReturnValue($jwksUri, 'keys');
- }
-
- private function extractPublicKeyFromCertificate($certificate)
- {
- $modulus = new BigInteger(JWT::urlsafeB64Decode($certificate['n']), 256);
- $exponent = new BigInteger(JWT::urlsafeB64Decode($certificate['e']), 256);
-
- $this->rsa->loadKey(compact('modulus', 'exponent'));
-
- return $this->rsa->getPublicKey();
- }
-
- public function getKidFromOpenIdToken($openIdToken)
- {
- return $this->callApiAndReturnValue(self::URL_TOKEN_INFO . '?id_token=' . $openIdToken, 'kid');
- }
-
- private function callApiAndReturnValue($url, $value)
- {
- $response = $this->guzzle->get($url);
-
- $data = json_decode($response->getBody(), true);
-
- return Arr::get($data, $value);
- }
-
- public function isCached()
- {
- return Cache::has(self::V3_CERTS);
- }
-}
diff --git a/src/TaskHandler.php b/src/TaskHandler.php
index 09341df..d56bd6a 100644
--- a/src/TaskHandler.php
+++ b/src/TaskHandler.php
@@ -1,118 +1,89 @@
client = $client;
- $this->request = $request;
- $this->jwt = $jwt;
- $this->publicKey = $publicKey;
- }
-
/**
- * @param $task
- * @throws CloudTasksException
+ * @var QueueConfig
*/
- public function handle($task = null)
- {
- $this->authorizeRequest();
-
- $task = $task ?: $this->captureTask();
-
- $this->handleTask($task);
- }
+ private array $config;
- /**
- * @throws CloudTasksException
- */
- public function authorizeRequest()
+ public function __construct(private readonly CloudTasksClient $client)
{
- if (!$this->request->hasHeader('Authorization')) {
- throw new CloudTasksException('Missing [Authorization] header');
- }
-
- $openIdToken = $this->request->bearerToken();
- $kid = $this->publicKey->getKidFromOpenIdToken($openIdToken);
- $publicKey = $this->publicKey->getPublicKey($kid);
-
- $decodedToken = $this->jwt->decode($openIdToken, $publicKey, ['RS256']);
-
- $this->validateToken($decodedToken);
+ //
}
- /**
- * https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken
- *
- * @param $openIdToken
- * @throws CloudTasksException
- */
- protected function validateToken($openIdToken)
+ public function handle(?string $task = null): void
{
- if (!in_array($openIdToken->iss, ['https://accounts.google.com/', 'accounts.google.com'])) {
- throw new CloudTasksException('The given OpenID token is not valid');
+ try {
+ $task = IncomingTask::fromJson($task ?: request()->getContent());
+ } catch (Exception $e) {
+ abort(422, $e->getMessage());
}
- if ($openIdToken->aud != Config::handler()) {
- throw new CloudTasksException('The given OpenID token is not valid');
- }
+ event(new TaskIncoming($task));
- if ($openIdToken->exp < time()) {
- throw new CloudTasksException('The given OpenID token has expired');
+ if (! CloudTasksApi::exists($task->fullyQualifiedTaskName())) {
+ abort(404);
}
- }
- /**
- * @throws CloudTasksException
- */
- private function captureTask()
- {
- $input = file_get_contents('php://input');
+ /** @var QueueConfig $config */
+ $config = config('queue.connections.'.$task->connection());
- if ($input === false) {
- throw new CloudTasksException('Could not read incoming task');
- }
+ $this->config = $config;
- $task = json_decode($input, true);
-
- if (is_null($task)) {
- throw new CloudTasksException('Could not decode incoming task');
- }
-
- return $task;
+ // We want to catch any errors so we have more fine-grained control over
+ // how tasks are retried. Cloud Tasks will retry the task if a 5xx status
+ // is returned. Because we manually manage retries by releasing jobs,
+ // we never want to return a 5xx status as that will result in duplicate
+ // job attempts.
+ rescue(fn () => $this->run($task));
}
- /**
- * @param $task
- * @throws CloudTasksException
- */
- private function handleTask($task)
+ private function run(IncomingTask $task): void
{
- $job = new CloudTasksJob($task, request()->header('X-CloudTasks-TaskRetryCount'));
-
- $worker = $this->getQueueWorker();
-
- $worker->process('cloudtasks', $job, new WorkerOptions());
+ $queue = tap(new CloudTasksQueue($this->config, $this->client))->setConnectionName($task->connection());
+
+ $job = new CloudTasksJob(
+ container: Container::getInstance(),
+ driver: $queue,
+ job: $task->toArray(),
+ connectionName: $task->connection(),
+ queue: $task->queue(),
+ );
+
+ $job->setAttempts($job->attempts() + 1);
+
+ /** @var Worker $worker */
+ $worker = app('cloud-tasks.worker');
+
+ $worker->process(
+ connectionName: $job->getConnectionName(),
+ job: $job,
+ options: CloudTasksQueue::getWorkerOptionsCallback() ? (CloudTasksQueue::getWorkerOptionsCallback())($task) : $this->getWorkerOptions()
+ );
}
- /**
- * @return Worker
- */
- private function getQueueWorker()
+ public function getWorkerOptions(): WorkerOptions
{
- return app('queue.worker');
+ $options = new WorkerOptions;
+
+ if (isset($this->config['backoff'])) {
+ $options->backoff = $this->config['backoff'];
+ }
+
+ return $options;
}
}
diff --git a/src/Worker.php b/src/Worker.php
new file mode 100644
index 0000000..457b179
--- /dev/null
+++ b/src/Worker.php
@@ -0,0 +1,75 @@
+timeoutForJob($job, $options), 0));
+
+ app(ExceptionHandler::class)->reportable(
+ fn (FatalError $error) => $this->onFatalError($error, $job, $options)
+ );
+
+ parent::process($connectionName, $job, $options);
+ }
+
+ private function onFatalError(FatalError $error, CloudTasksJob $job, WorkerOptions $options): bool
+ {
+ if (fnmatch('Maximum execution time * exceeded', $error->getMessage())) {
+ $this->onJobTimedOut($job, $options);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private function onJobTimedOut(CloudTasksJob $job, WorkerOptions $options): void
+ {
+ $this->markJobAsFailedIfWillExceedMaxAttempts(
+ $job->getConnectionName(), $job, (int) $options->maxTries, $e = $this->timeoutExceededException($job)
+ );
+
+ $this->markJobAsFailedIfWillExceedMaxExceptions(
+ $job->getConnectionName(), $job, $e
+ );
+
+ $this->markJobAsFailedIfItShouldFailOnTimeout(
+ $job->getConnectionName(), $job, $e
+ );
+
+ $this->events->dispatch(new JobTimedOut(
+ $job->getConnectionName(), $job
+ ));
+
+ if (! $job->isDeleted() && ! $job->isReleased() && ! $job->hasFailed()) {
+ $job->release($this->calculateBackoff($job, $options));
+ }
+ }
+}
diff --git a/tests/CloudTasksApiTest.php b/tests/CloudTasksApiTest.php
new file mode 100644
index 0000000..5b42338
--- /dev/null
+++ b/tests/CloudTasksApiTest.php
@@ -0,0 +1,141 @@
+fail('Missing ['.$env.'] environment variable.');
+ }
+ }
+
+ $this->setConfigValue('project', env('CI_CLOUD_TASKS_PROJECT_ID'));
+ $this->setConfigValue('queue', env('CI_CLOUD_TASKS_QUEUE'));
+ $this->setConfigValue('location', env('CI_CLOUD_TASKS_LOCATION'));
+ $this->setConfigValue('service_account_email', env('CI_CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL'));
+
+ $this->client = new CloudTasksClient;
+
+ }
+
+ #[Test]
+ public function custom_client_options_can_be_added()
+ {
+ // Arrange
+ config()->set('cloud-tasks.client_options', [
+ 'credentials' => __DIR__.'/Support/gcloud-key-dummy.json',
+ ]);
+
+ // Act
+ $export = var_export(app(CloudTasksClient::class), true);
+
+ // Assert
+
+ // CloudTasksClient makes it a bit difficult to read its properties, so this will have to do...
+ $this->assertStringContainsString('info@stackkit.io', $export);
+ $this->assertStringContainsString('PRIVATE KEY', $export);
+ }
+
+ #[Test]
+ public function test_create_task()
+ {
+ // Arrange
+ $httpRequest = new HttpRequest;
+ $httpRequest->setHttpMethod(HttpMethod::GET);
+ $httpRequest->setUrl('https://example.com/');
+
+ $cloudTask = new Task;
+ $cloudTask->setHttpRequest($httpRequest);
+
+ // Act
+ $task = CloudTasksApi::createTask(
+ $this->client->queueName(
+ env('CI_CLOUD_TASKS_PROJECT_ID'),
+ env('CI_CLOUD_TASKS_LOCATION'),
+ env('CI_CLOUD_TASKS_QUEUE')
+ ),
+ $cloudTask
+ );
+ $taskName = $task->getName();
+
+ // Assert
+ $this->assertMatchesRegularExpression(
+ '/projects\/'.env('CI_CLOUD_TASKS_PROJECT_ID').'\/locations\/'.env('CI_CLOUD_TASKS_LOCATION').'\/queues\/'.env('CI_CLOUD_TASKS_QUEUE').'\/tasks\/\d+$/',
+ $taskName
+ );
+ }
+
+ #[Test]
+ public function test_delete_task_on_non_existing_task()
+ {
+ // Assert
+ $this->expectException(ApiException::class);
+ $this->expectExceptionMessage('Requested entity was not found.');
+
+ // Act
+ CloudTasksApi::deleteTask(
+ $this->client->taskName(
+ env('CI_CLOUD_TASKS_PROJECT_ID'),
+ env('CI_CLOUD_TASKS_LOCATION'),
+ env('CI_CLOUD_TASKS_QUEUE'),
+ 'non-existing-id'
+ ),
+ );
+
+ }
+
+ #[Test]
+ public function test_delete_task()
+ {
+ // Arrange
+ $httpRequest = new HttpRequest;
+ $httpRequest->setHttpMethod(HttpMethod::GET);
+ $httpRequest->setUrl('https://example.com/');
+
+ $cloudTask = new Task;
+ $cloudTask->setHttpRequest($httpRequest);
+ $cloudTask->setScheduleTime(new Timestamp(['seconds' => time() + 10]));
+
+ $task = CloudTasksApi::createTask(
+ $this->client->queueName(
+ env('CI_CLOUD_TASKS_PROJECT_ID'),
+ env('CI_CLOUD_TASKS_LOCATION'),
+ env('CI_CLOUD_TASKS_QUEUE')
+ ),
+ $cloudTask
+ );
+
+ // Act
+ $fresh = CloudTasksApi::getTask($task->getName());
+ $this->assertInstanceOf(Task::class, $fresh);
+
+ CloudTasksApi::deleteTask($task->getName());
+
+ $this->expectException(ApiException::class);
+ $this->expectExceptionMessage('NOT_FOUND');
+ CloudTasksApi::getTask($task->getName());
+ }
+}
diff --git a/tests/ConfigHandlerTest.php b/tests/ConfigHandlerTest.php
new file mode 100644
index 0000000..9a3f9ca
--- /dev/null
+++ b/tests/ConfigHandlerTest.php
@@ -0,0 +1,68 @@
+setConfigValue('handler', $handler);
+
+ $this->dispatch(new SimpleJob);
+
+ CloudTasksApi::assertTaskCreated(function (Task $task) use ($expectedHandler) {
+ return $task->getHttpRequest()->getUrl() === $expectedHandler;
+ });
+ }
+
+ #[Test]
+ public function the_handle_route_task_uri_can_be_configured(): void
+ {
+ CloudTasksApi::fake();
+
+ $this->app['config']->set('cloud-tasks.uri', 'my-custom-route');
+
+ $this->dispatch(new SimpleJob);
+
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getHttpRequest()->getUrl() === 'https://docker.for.mac.localhost:8080/my-custom-route';
+ });
+ }
+
+ #[Test]
+ public function the_handle_route_task_uri_in_combination_with_path_can_be_configured(): void
+ {
+ CloudTasksApi::fake();
+
+ $this->setConfigValue('handler', 'https://example.com/api');
+ $this->app['config']->set('cloud-tasks.uri', 'my-custom-route');
+
+ $this->dispatch(new SimpleJob);
+
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getHttpRequest()->getUrl() === 'https://example.com/api/my-custom-route';
+ });
+ }
+
+ public static function handlerDataProvider(): array
+ {
+ return [
+ ['https://example.com/', 'https://example.com/handle-task'],
+ ['https://example.com/my/path', 'https://example.com/my/path/handle-task'],
+ ['https://example.com/trailing/slashes//', 'https://example.com/trailing/slashes/handle-task'],
+ ['https://example.com/handle-task', 'https://example.com/handle-task'],
+ ['https://example.com/handle-task/', 'https://example.com/handle-task'],
+ ];
+ }
+}
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php
deleted file mode 100644
index 82cf94d..0000000
--- a/tests/ConfigTest.php
+++ /dev/null
@@ -1,76 +0,0 @@
-setConfigValue('credentials', '');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::invalidCredentials());
-
- SimpleJob::dispatch();
- }
-
- /** @test */
- public function credentials_file_must_exist()
- {
- $this->setConfigValue('credentials', 'doesnotexist.json');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::credentialsFileDoesNotExist());
-
- SimpleJob::dispatch();
- }
-
- /** @test */
- public function project_is_required()
- {
- $this->setConfigValue('project', '');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::invalidProject());
-
- SimpleJob::dispatch();
- }
-
- /** @test */
- public function location_is_required()
- {
- $this->setConfigValue('location', '');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::invalidLocation());
-
- SimpleJob::dispatch();
- }
-
- /** @test */
- public function handler_is_required()
- {
- $this->setConfigValue('handler', '');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::invalidHandler());
-
- SimpleJob::dispatch();
- }
-
- /** @test */
- public function service_email_is_required()
- {
- $this->setConfigValue('service_account_email', '');
-
- $this->expectException(Error::class);
- $this->expectExceptionMessage(Errors::invalidServiceAccountEmail());
-
- SimpleJob::dispatch();
- }
-}
diff --git a/tests/GooglePublicKeyTest.php b/tests/GooglePublicKeyTest.php
deleted file mode 100644
index f219375..0000000
--- a/tests/GooglePublicKeyTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-guzzle = Mockery::mock(new Client());
-
- $this->publicKey = new OpenIdVerificator($this->guzzle, new RSA());
- }
-
- /** @test */
- public function it_fetches_the_gcloud_public_key()
- {
- $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $this->publicKey->getPublicKey());
- }
-
- /** @test */
- public function it_caches_the_gcloud_public_key()
- {
- $this->assertFalse($this->publicKey->isCached());
-
- $this->publicKey->getPublicKey();
-
- $this->assertTrue($this->publicKey->isCached());
- }
-
- /** @test */
- public function it_will_return_the_cached_gcloud_public_key()
- {
- $this->publicKey->getPublicKey();
-
- $this->publicKey->getPublicKey();
-
- $this->guzzle->shouldHaveReceived('get')->twice();
- }
-}
diff --git a/tests/IncomingTaskTest.php b/tests/IncomingTaskTest.php
new file mode 100644
index 0000000..ff29f0d
--- /dev/null
+++ b/tests/IncomingTaskTest.php
@@ -0,0 +1,136 @@
+withTaskType($taskType);
+ Str::createUlidsUsingSequence(['01HSR4V9QE2F4T0K8RBAYQ88KE']);
+
+ // Act
+ $this->dispatch(new $job)->run();
+
+ // Assert
+ Event::assertDispatched(function (TaskIncoming $event) use ($job) {
+ return $event->task->fullyQualifiedTaskName() === 'projects/my-test-project/locations/europe-west6/queues/barbequeue/tasks/01HSR4V9QE2F4T0K8RBAYQ88KE-'.class_basename($job)
+ && $event->task->connection() === 'my-cloudtasks-connection'
+ && $event->task->queue() === 'barbequeue';
+ });
+ }
+
+ #[Test]
+ #[TestWith([SimpleJob::class, 'cloudtasks'])]
+ #[TestWith([SimpleJob::class, 'appengine'])]
+ #[TestWith([EncryptedJob::class, 'cloudtasks'])]
+ #[TestWith([EncryptedJob::class, 'appengine'])]
+ public function it_reads_the_custom_queue(string $job, string $taskType)
+ {
+ // Arrange
+ $this->withTaskType($taskType);
+
+ // Act
+ $this->dispatch((new $job)->onQueue('other-queue'))->run();
+
+ // Assert
+ Event::assertDispatched(function (TaskIncoming $event) {
+ return $event->task->queue() === 'other-queue';
+ });
+ }
+
+ #[Test]
+ #[TestWith([SimpleJob::class, 'cloudtasks'])]
+ #[TestWith([SimpleJob::class, 'appengine'])]
+ #[TestWith([EncryptedJob::class, 'cloudtasks'])]
+ #[TestWith([EncryptedJob::class, 'appengine'])]
+ public function it_reads_the_custom_connection(string $job, string $taskType)
+ {
+ // Arrange
+ $this->withTaskType($taskType);
+
+ // Act
+ $this->dispatch((new $job)->onConnection('my-other-cloudtasks-connection'))->run();
+
+ // Assert
+ Event::assertDispatched(function (TaskIncoming $event) {
+ return $event->task->connection() === 'my-other-cloudtasks-connection'
+ && $event->task->queue() === 'other-barbequeue';
+ });
+ }
+
+ #[Test]
+ #[TestWith([SimpleJob::class, 'cloudtasks'])]
+ #[TestWith([SimpleJob::class, 'appengine'])]
+ #[TestWith([EncryptedJob::class, 'cloudtasks'])]
+ #[TestWith([EncryptedJob::class, 'appengine'])]
+ public function it_reads_the_custom_connection_with_custom_queue(string $job, string $taskType)
+ {
+ // Arrange
+ $this->withTaskType($taskType);
+
+ // Act
+ $this->dispatch(
+ (new $job)
+ ->onConnection('my-other-cloudtasks-connection')
+ ->onQueue('custom-barbequeue')
+ )->run();
+
+ // Assert
+ Event::assertDispatched(function (TaskIncoming $event) {
+ return $event->task->connection() === 'my-other-cloudtasks-connection'
+ && $event->task->queue() === 'custom-barbequeue';
+ });
+ }
+
+ #[Test]
+ public function it_can_convert_the_incoming_task_to_array()
+ {
+ // Act
+ $incomingTask = IncomingTask::fromJson('{"internal":{"connection":"my-other-cloudtasks-connection","queue":"custom-barbequeue","taskName":"projects/my-test-project/locations/europe-west6/queues/barbequeue/tasks/01HSR4V9QE2F4T0K8RBAYQ88KE-SimpleJob"}}');
+
+ // Act
+ $array = $incomingTask->toArray();
+
+ // Assert
+ $this->assertIsArray($array);
+ $this->assertSame('my-other-cloudtasks-connection', $array['internal']['connection']);
+ }
+
+ #[Test]
+ public function test_invalid_function()
+ {
+ // Assert
+ $this->expectExceptionMessage('Invalid task payload.');
+
+ // Act
+ IncomingTask::fromJson('{ invalid json }');
+ }
+}
diff --git a/tests/QueueAppEngineTest.php b/tests/QueueAppEngineTest.php
new file mode 100644
index 0000000..addf4b4
--- /dev/null
+++ b/tests/QueueAppEngineTest.php
@@ -0,0 +1,69 @@
+withTaskType('appengine');
+ }
+
+ #[Test]
+ public function an_app_engine_http_request_with_the_handler_url_is_made()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch(new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getAppEngineHttpRequest()->getRelativeUri() === '/handle-task';
+ });
+ }
+
+ #[Test]
+ public function it_routes_to_the_service()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch(new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getAppEngineHttpRequest()->getAppEngineRouting()->getService() === 'api';
+ });
+ }
+
+ #[Test]
+ public function it_contains_the_payload()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch($job = new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) use ($job): bool {
+ $decoded = json_decode($task->getAppEngineHttpRequest()->getBody(), true);
+
+ return $decoded['displayName'] === SimpleJob::class
+ && $decoded['job'] === 'Illuminate\Queue\CallQueuedHandler@call'
+ && $decoded['data']['command'] === serialize($job);
+ });
+ }
+}
diff --git a/tests/QueueTest.php b/tests/QueueTest.php
index f8ec4b0..1e9110e 100644
--- a/tests/QueueTest.php
+++ b/tests/QueueTest.php
@@ -1,127 +1,616 @@
client = Mockery::mock(CloudTasksClient::class)->makePartial();
- $this->http = Mockery::mock(new HttpRequest)->makePartial();
- $this->task = Mockery::mock(new Task);
+ // Act
+ $this->dispatch(new SimpleJob);
- $this->app->instance(CloudTasksClient::class, $this->client);
- $this->app->instance(HttpRequest::class, $this->http);
- $this->app->instance(Task::class, $this->task);
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getUrl() === 'https://docker.for.mac.localhost:8080/handle-task';
+ });
+ }
+
+ #[Test]
+ public function it_posts_to_the_handler()
+ {
+ // Arrange
+ CloudTasksApi::fake();
- // ensure we don't actually call the Google API
- $this->client->shouldReceive('createTask')->andReturnNull();
+ // Act
+ $this->dispatch(new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getHttpMethod() === HttpMethod::POST;
+ });
}
- /** @test */
- public function a_http_request_with_the_handler_url_is_made()
+ #[Test]
+ public function it_posts_to_the_configured_handler_url()
{
- SimpleJob::dispatch();
+ // Arrange
+ $this->setConfigValue('handler', 'https://docker.for.mac.localhost:8081/');
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch(new SimpleJob);
- $this->http
- ->shouldHaveReceived('setUrl')
- ->with('https://localhost/my-handler')
- ->once();
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getUrl() === 'https://docker.for.mac.localhost:8081/handle-task';
+ });
}
- /** @test */
- public function it_posts_to_the_handler()
+ #[Test]
+ public function it_posts_to_the_callback_handler_url()
{
- SimpleJob::dispatch();
+ // Arrange
+ $this->setConfigValue('handler', 'https://docker.for.mac.localhost:8081/');
+ CloudTasksApi::fake();
+ CloudTasksQueue::configureHandlerUrlUsing(static fn (SimpleJob $job) => 'https://example.com/api/my-custom-route?job='.$job->id);
+
+ // Act
+ $job = new SimpleJob;
+ $job->id = 1;
+ $this->dispatch($job);
- $this->http->shouldHaveReceived('setHttpMethod')->with(HttpMethod::POST)->once();
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getUrl() === 'https://example.com/api/my-custom-route?job=1';
+ });
}
- /** @test */
+ #[Test]
public function it_posts_the_serialized_job_payload_to_the_handler()
{
- $job = new SimpleJob();
- $job->dispatch();
+ // Arrange
+ CloudTasksApi::fake();
- $this->http->shouldHaveReceived('setBody')->with(Mockery::on(function ($payload) use ($job) {
- $decoded = json_decode($payload, true);
+ // Act
+ $this->dispatch($job = new SimpleJob);
- if ($decoded['displayName'] != 'Tests\Support\SimpleJob') {
- return false;
- }
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) use ($job): bool {
+ $decoded = json_decode($task->getHttpRequest()->getBody(), true);
- if ($decoded['job'] != 'Illuminate\Queue\CallQueuedHandler@call') {
- return false;
- }
+ return $decoded['displayName'] === SimpleJob::class
+ && $decoded['job'] === 'Illuminate\Queue\CallQueuedHandler@call'
+ && $decoded['data']['command'] === serialize($job);
+ });
+ }
- if ($decoded['data']['commandName'] != 'Tests\Support\SimpleJob') {
- return false;
- }
+ #[Test]
+ public function it_will_set_the_scheduled_time_when_dispatching_later()
+ {
+ // Arrange
+ CloudTasksApi::fake();
- if ($decoded['data']['command'] != serialize($job)) {
- return false;
- }
+ // Act
+ $inFiveMinutes = now()->addMinutes(5);
+ $this->dispatch((new SimpleJob)->delay($inFiveMinutes));
- return true;
- }));
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) use ($inFiveMinutes): bool {
+ return $task->getScheduleTime()->getSeconds() === $inFiveMinutes->timestamp;
+ });
}
- /** @test */
- public function it_creates_a_task_containing_the_http_request()
+ #[Test]
+ public function it_posts_the_task_the_correct_queue()
{
- $this->task->shouldReceive('setHttpRequest')->once()->with($this->http);
+ // Arrange
+ CloudTasksApi::fake();
+
+ $closure = fn () => 'closure job';
+ $closureDisplayName = CallQueuedClosure::create($closure)->displayName();
+
+ // Act
+ $this->dispatch((new SimpleJob));
+ $this->dispatch((new FailingJob)->onQueue('my-special-queue'));
+ $this->dispatch($closure);
+ $this->dispatch($closure, 'my-special-queue');
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task, string $queueName): bool {
+ $decoded = json_decode($task->getHttpRequest()->getBody(), true);
+ $command = IncomingTask::fromJson($task->getHttpRequest()->getBody())->command();
+
+ return $decoded['displayName'] === SimpleJob::class
+ && $command['queue'] === 'barbequeue'
+ && $queueName === 'projects/my-test-project/locations/europe-west6/queues/barbequeue';
+ });
+
+ CloudTasksApi::assertTaskCreated(function (Task $task, string $queueName): bool {
+ $decoded = json_decode($task->getHttpRequest()->getBody(), true);
+ $command = IncomingTask::fromJson($task->getHttpRequest()->getBody())->command();
+
+ return $decoded['displayName'] === FailingJob::class
+ && $command['queue'] === 'my-special-queue'
+ && $queueName === 'projects/my-test-project/locations/europe-west6/queues/my-special-queue';
+ });
+
+ CloudTasksApi::assertTaskCreated(function (Task $task, string $queueName) use ($closureDisplayName): bool {
+ $decoded = json_decode($task->getHttpRequest()->getBody(), true);
+ $command = IncomingTask::fromJson($task->getHttpRequest()->getBody())->command();
+
+ return $decoded['displayName'] === $closureDisplayName
+ && $command['queue'] === 'barbequeue'
+ && $queueName === 'projects/my-test-project/locations/europe-west6/queues/barbequeue';
+ });
+
+ CloudTasksApi::assertTaskCreated(function (Task $task, string $queueName) use ($closureDisplayName): bool {
+ $decoded = json_decode($task->getHttpRequest()->getBody(), true);
+ $command = IncomingTask::fromJson($task->getHttpRequest()->getBody())->command();
+
+ return $decoded['displayName'] === $closureDisplayName
+ && $command['queue'] === 'my-special-queue'
+ && $queueName === 'projects/my-test-project/locations/europe-west6/queues/my-special-queue';
+ });
+ }
+
+ #[Test]
+ public function it_can_dispatch_after_commit_inline()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake();
+
+ // Act & Assert
+ Event::assertNotDispatched(JobQueued::class);
+ DB::beginTransaction();
+ SimpleJob::dispatch()->afterCommit();
+ Event::assertNotDispatched(JobQueued::class);
+ while (DB::transactionLevel() !== 0) {
+ DB::commit();
+ }
+ Event::assertDispatched(JobQueued::class, function (JobQueued $event) {
+ return $event->job instanceof SimpleJob;
+ });
+ }
+
+ #[Test]
+ public function it_can_dispatch_after_commit_through_config()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake();
+ $this->setConfigValue('after_commit', true);
+
+ // Act & Assert
+ Event::assertNotDispatched(JobQueued::class);
+ DB::beginTransaction();
SimpleJob::dispatch();
+ Event::assertNotDispatched(JobQueued::class);
+ while (DB::transactionLevel() !== 0) {
+ DB::commit();
+ }
+ Event::assertDispatched(JobQueued::class, function (JobQueued $event) {
+ return $event->job instanceof SimpleJob;
+ });
}
- /** @test */
- public function it_will_set_the_scheduled_time_when_dispatching_later()
+ #[Test]
+ public function jobs_can_be_released()
{
- $inFiveMinutes = Carbon::now()->addMinutes(5);
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake([
+ JobReleasedAfterException::class,
+ JobReleased::class,
+ ]);
+
+ // Act
+ $this->dispatch(new JobThatWillBeReleased)
+ ->runAndGetReleasedJob()
+ ->run();
- SimpleJob::dispatch()->delay($inFiveMinutes);
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ $body = $task->getHttpRequest()->getBody();
+ $decoded = json_decode($body, true);
- $this->task->shouldHaveReceived('setScheduleTime')->once()->with(Mockery::on(function (Timestamp $timestamp) use ($inFiveMinutes) {
- return $timestamp->getSeconds() === $inFiveMinutes->timestamp;
- }));
+ return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
+ && $decoded['internal']['attempts'] === 1;
+ });
+
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ $body = $task->getHttpRequest()->getBody();
+ $decoded = json_decode($body, true);
+
+ return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
+ && $decoded['internal']['attempts'] === 2;
+ });
}
- /** @test */
- public function it_posts_the_task_the_correct_queue()
+ #[Test]
+ public function jobs_can_be_released_with_a_delay()
{
- SimpleJob::dispatch();
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake([
+ JobReleasedAfterException::class,
+ JobReleased::class,
+ ]);
+ Carbon::setTestNow(now()->addDay());
- $this->client
- ->shouldHaveReceived('createTask')
- ->withArgs(function ($queueName) {
- return $queueName === 'projects/test-project/locations/europe-west6/queues/test-queue';
- });
+ // Act
+ $this->dispatch(new JobThatWillBeReleased(15))->run();
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ $body = $task->getHttpRequest()->getBody();
+ $decoded = json_decode($body, true);
+
+ $scheduleTime = $task->getScheduleTime() ? $task->getScheduleTime()->getSeconds() : null;
+
+ return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
+ && $decoded['internal']['attempts'] === 1
+ && $scheduleTime === now()->getTimestamp() + 15;
+ });
}
- /** @test */
- public function it_posts_the_correct_task_the_queue()
+ #[Test]
+ public function test_default_backoff()
{
- SimpleJob::dispatch();
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act
+ $this->dispatch(new FailingJob)->run();
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return is_null($task->getScheduleTime());
+ });
+ }
+
+ #[Test]
+ public function test_backoff_from_queue_config()
+ {
+ // Arrange
+ Carbon::setTestNow(now()->addDay());
+ $this->setConfigValue('backoff', 123);
+ CloudTasksApi::fake();
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act
+ $this->dispatch(new FailingJob)->run();
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getScheduleTime()
+ && $task->getScheduleTime()->getSeconds() === now()->getTimestamp() + 123;
+ });
+ }
+
+ #[Test]
+ public function test_backoff_from_job()
+ {
+ // Arrange
+ Carbon::setTestNow(now()->addDay());
+ CloudTasksApi::fake();
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act
+ $failingJob = new FailingJob;
+ $failingJob->backoff = 123;
+ $this->dispatch($failingJob)->run();
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getScheduleTime()
+ && $task->getScheduleTime()->getSeconds() === now()->getTimestamp() + 123;
+ });
+ }
+
+ #[Test]
+ public function test_exponential_backoff_from_job_method()
+ {
+ // Arrange
+ Carbon::setTestNow(now()->addDay());
+ CloudTasksApi::fake();
+
+ // Act
+ $releasedJob = $this->dispatch(new FailingJobWithExponentialBackoff)
+ ->runAndGetReleasedJob();
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $releasedJob->run();
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getScheduleTime()
+ && $task->getScheduleTime()->getSeconds() === now()->getTimestamp() + 50;
+ });
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getScheduleTime()
+ && $task->getScheduleTime()->getSeconds() === now()->getTimestamp() + 60;
+ });
+ CloudTasksApi::assertTaskCreated(function (Task $task) {
+ return $task->getScheduleTime()
+ && $task->getScheduleTime()->getSeconds() === now()->getTimestamp() + 70;
+ });
+ }
+
+ #[Test]
+ public function test_failing_method_on_job()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobOutput::class);
+
+ // Act
+ $this->dispatch(new FailingJob)
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob();
+
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'FailingJob:failed');
+ }
+
+ #[Test]
+ public function test_queue_before_and_after_hooks()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobOutput::class);
+
+ // Act
+ Queue::before(function (JobProcessing $event) {
+ event(new JobOutput('Queue::before:'.$event->job->payload()['data']['commandName']));
+ });
+ Queue::after(function (JobProcessed $event) {
+ event(new JobOutput('Queue::after:'.$event->job->payload()['data']['commandName']));
+ });
+ $this->dispatch(new SimpleJob)->run();
+
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'Queue::before:Tests\Support\SimpleJob');
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'Queue::after:Tests\Support\SimpleJob');
+ }
+
+ #[Test]
+ public function test_queue_looping_hook_not_supported_with_this_package()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobOutput::class);
+
+ // Act
+ Queue::looping(function () {
+ event(new JobOutput('Queue::looping'));
+ });
+ $this->dispatch(new SimpleJob)->run();
+
+ // Assert
+ Event::assertDispatchedTimes(JobOutput::class, times: 1);
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'SimpleJob:success');
+ }
+
+ #[Test]
+ public function test_ignoring_jobs_with_deleted_models()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobOutput::class);
+
+ $user1 = User::create([
+ 'name' => 'John',
+ 'email' => 'johndoe@example.com',
+ 'password' => bcrypt('test'),
+ ]);
+
+ $user2 = User::create([
+ 'name' => 'Jane',
+ 'email' => 'janedoe@example.com',
+ 'password' => bcrypt('test'),
+ ]);
+
+ // Act
+ $this->dispatch(new UserJob($user1))->run();
+
+ $job = $this->dispatch(new UserJob($user2));
+ $user2->delete();
+ $job->run();
+
+ // Act
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'UserJob:John');
+ CloudTasksApi::assertTaskNotDeleted($job->task->getName());
+ }
+
+ #[Test]
+ public function it_adds_a_pre_defined_task_name()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Str::createUlidsUsingSequence(['01HSR4V9QE2F4T0K8RBAYQ88KE']);
+
+ // Act
+ $this->dispatch((new SimpleJob));
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getName() === 'projects/my-test-project/locations/europe-west6/queues/barbequeue/tasks/01HSR4V9QE2F4T0K8RBAYQ88KE-SimpleJob';
+ });
+ }
+
+ #[Test]
+ public function headers_can_be_added_to_the_task()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ CloudTasksQueue::setTaskHeadersUsing(static fn () => [
+ 'X-MyHeader' => 'MyValue',
+ ]);
+
+ $this->dispatch((new SimpleJob));
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getHeaders()['X-MyHeader'] === 'MyValue';
+ });
+ }
+
+ #[Test]
+ public function headers_can_be_added_to_the_task_with_job_context()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ CloudTasksQueue::setTaskHeadersUsing(static fn (array $payload) => [
+ 'X-MyHeader' => $payload['displayName'],
+ ]);
+
+ $this->dispatch((new SimpleJob));
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getHttpRequest()->getHeaders()['X-MyHeader'] === SimpleJob::class;
+ });
+ }
+
+ #[Test]
+ public function batched_jobs_with_custom_queue_are_dispatched_on_the_custom_queue()
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch(Bus::batch([
+ tap(new SimpleJob, function (SimpleJob $job) {
+ $job->queue = 'my-queue1';
+ }),
+ tap(new SimpleJobWithTimeout, function (SimpleJob $job) {
+ $job->queue = 'my-queue2';
+ }),
+ ])->onQueue('my-batch-queue'));
+
+ // Assert
+ CloudTasksApi::assertCreatedTaskCount(2);
+
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return str_contains($task->getName(), 'SimpleJob')
+ && str_contains($task->getName(), 'my-batch-queue');
+ });
+
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return str_contains($task->getName(), 'SimpleJobWithTimeout')
+ && str_contains($task->getName(), 'my-batch-queue');
+ });
+ }
+
+ #[Test]
+ public function it_can_dispatch_closures(): void
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ Event::fake(JobOutput::class);
+
+ // Act
+ $this->dispatch(function () {
+ event(new JobOutput('ClosureJob:success'));
+ })->run();
+
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'ClosureJob:success');
+ }
+
+ #[Test]
+ public function task_has_no_dispatch_deadline_by_default(): void
+ {
+ // Arrange
+ CloudTasksApi::fake();
+
+ // Act
+ $this->dispatch(new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getDispatchDeadline() === null;
+ });
+ }
+
+ #[Test]
+ public function task_has_no_dispatch_deadline_if_config_is_empty(): void
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ $this->setConfigValue('dispatch_deadline', null);
+
+ // Act
+ $this->dispatch(new SimpleJob);
+
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getDispatchDeadline() === null;
+ });
+ }
+
+ #[Test]
+ public function task_has_configured_dispatch_deadline(): void
+ {
+ // Arrange
+ CloudTasksApi::fake();
+ $this->setConfigValue('dispatch_deadline', 1800);
+
+ // Act
+ $this->dispatch(new SimpleJob);
- $this->client
- ->shouldHaveReceived('createTask')
- ->withArgs(function ($queueName, $task) {
- return $task === $this->task;
- });
+ // Assert
+ CloudTasksApi::assertTaskCreated(function (Task $task): bool {
+ return $task->getDispatchDeadline()->getSeconds() === 1800;
+ });
}
}
diff --git a/tests/Support/BaseJob.php b/tests/Support/BaseJob.php
new file mode 100644
index 0000000..b72e1d8
--- /dev/null
+++ b/tests/Support/BaseJob.php
@@ -0,0 +1,17 @@
+payload = $payload;
+ $this->task = $task;
+ $this->testCase = $testCase;
+ }
+
+ public function run(): void
+ {
+ $header = match (true) {
+ $this->task->hasHttpRequest() => 'HTTP_X_CLOUDTASKS_TASKNAME',
+ $this->task->hasAppEngineHttpRequest() => 'HTTP_X_APPENGINE_TASKNAME',
+ default => throw new Error('Task does not have a request.'),
+ };
+
+ $this->testCase->call(
+ method: 'POST',
+ uri: route('cloud-tasks.handle-task'),
+ server: [
+ $header => (string) str($this->task->getName())->after('/tasks/'),
+ ],
+ content: $this->payload,
+ );
+ }
+
+ public function runAndGetReleasedJob(): self
+ {
+ $this->run();
+
+ $releasedTask = end($this->testCase->createdTasks);
+
+ if (! $releasedTask) {
+ $this->testCase->fail('No task was released.');
+ }
+
+ $payload = $releasedTask->getAppEngineHttpRequest()?->getBody()
+ ?: $releasedTask->getHttpRequest()->getBody();
+
+ return new self(
+ $payload,
+ $releasedTask,
+ $this->testCase
+ );
+ }
+}
diff --git a/tests/Support/EncryptedJob.php b/tests/Support/EncryptedJob.php
new file mode 100644
index 0000000..2ba3868
--- /dev/null
+++ b/tests/Support/EncryptedJob.php
@@ -0,0 +1,15 @@
+addMinutes(5);
+ }
+}
diff --git a/tests/Support/FailingJobWithNoMaxTries.php b/tests/Support/FailingJobWithNoMaxTries.php
new file mode 100644
index 0000000..65273ee
--- /dev/null
+++ b/tests/Support/FailingJobWithNoMaxTries.php
@@ -0,0 +1,10 @@
+addMinutes(5);
+ }
+}
diff --git a/tests/Support/FailingJobWithUnlimitedTries.php b/tests/Support/FailingJobWithUnlimitedTries.php
new file mode 100644
index 0000000..7f92520
--- /dev/null
+++ b/tests/Support/FailingJobWithUnlimitedTries.php
@@ -0,0 +1,10 @@
+release($this->releaseDelay);
+ }
+}
diff --git a/tests/Support/SimpleJob.php b/tests/Support/SimpleJob.php
index 4a2c8cf..007a308 100644
--- a/tests/Support/SimpleJob.php
+++ b/tests/Support/SimpleJob.php
@@ -1,17 +1,14 @@
send(new TestMailable());
+ event(new JobOutput('SimpleJob:success'));
}
}
diff --git a/tests/Support/SimpleJobWithTimeout.php b/tests/Support/SimpleJobWithTimeout.php
new file mode 100644
index 0000000..641cef3
--- /dev/null
+++ b/tests/Support/SimpleJobWithTimeout.php
@@ -0,0 +1,20 @@
+ __FILE__,
+ 'line' => __LINE__,
+ ]);
+ }
+}
diff --git a/tests/Support/TestMailable.php b/tests/Support/TestMailable.php
deleted file mode 100644
index cab9d6b..0000000
--- a/tests/Support/TestMailable.php
+++ /dev/null
@@ -1,10 +0,0 @@
-user = $user;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ event(new JobOutput('UserJob:'.$this->user->name));
+ }
+}
diff --git a/tests/Support/gcloud-key-dummy.json b/tests/Support/gcloud-key-dummy.json
new file mode 100644
index 0000000..95530c7
--- /dev/null
+++ b/tests/Support/gcloud-key-dummy.json
@@ -0,0 +1,5 @@
+{
+ "type": "service_account",
+ "client_email": "info@stackkit.io",
+ "private_key": "PRIVATE KEY"
+}
diff --git a/tests/Support/test-job-payload.json b/tests/Support/test-job-payload.json
deleted file mode 100644
index 5fff7d4..0000000
--- a/tests/Support/test-job-payload.json
+++ /dev/null
@@ -1 +0,0 @@
-{"uuid":"f4e1ea03-3ab9-45f8-a4a1-50218169472e","displayName":"Tests\\Support\\SimpleJob","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"maxExceptions":null,"delay":null,"timeout":null,"timeoutAt":null,"data":{"commandName":"Tests\\Support\\SimpleJob","command":"O:23:\"Tests\\Support\\SimpleJob\":8:{s:3:\"job\";N;s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:5:\"delay\";N;s:10:\"middleware\";a:0:{}s:7:\"chained\";a:0:{}}"}}
diff --git a/tests/TaskHandlerTest.php b/tests/TaskHandlerTest.php
index d3c84d8..a9ab021 100644
--- a/tests/TaskHandlerTest.php
+++ b/tests/TaskHandlerTest.php
@@ -1,137 +1,301 @@
request = request();
-
- // We don't have a valid token to test with, so for now act as if its always valid
- $this->app->instance(JWT::class, ($this->jwt = Mockery::mock(new JWT())->byDefault()->makePartial()));
- $this->jwt->shouldReceive('decode')->andReturn((object) [
- 'iss' => 'accounts.google.com',
- 'aud' => 'https://localhost/my-handler',
- 'exp' => time() + 10
- ])->byDefault();
-
- // Ensure we don't fetch the Google public key each test...
- $googlePublicKey = Mockery::mock(app(OpenIdVerificator::class));
- $googlePublicKey->shouldReceive('getPublicKey')->andReturnNull();
- $googlePublicKey->shouldReceive('getKidFromOpenIdToken')->andReturnNull();
-
- $this->handler = new TaskHandler(
- new CloudTasksClient([
- 'credentials' => __DIR__ . '/Support/gcloud-key-valid.json'
- ]),
- request(),
- $this->jwt,
- $googlePublicKey
- );
-
- $this->request->headers->add(['Authorization' => 'Bearer 123']);
+ // Act
+ $this->dispatch(new SimpleJob)->run();
+
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'SimpleJob:success');
}
- /** @test */
- public function it_needs_an_authorization_header()
+ #[Test]
+ public function it_can_run_a_task_using_the_task_connection()
{
- $this->request->headers->remove('Authorization');
+ // Arrange
+
+ Event::fake(JobOutput::class);
+ $this->app['config']->set('queue.default', 'non-existing-connection');
- $this->expectException(CloudTasksException::class);
- $this->expectExceptionMessage('Missing [Authorization] header');
+ // Act
+ $job = new SimpleJob;
+ $job->connection = 'my-cloudtasks-connection';
+ $this->dispatch($job)->run();
- $this->handler->handle();
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'SimpleJob:success');
}
- /** @test */
- public function the_authorization_header_must_contain_a_valid_gcloud_token()
+ #[Test]
+ public function after_max_attempts_it_will_log_to_failed_table()
{
- request()->headers->add([
- 'Authorization' => 'Bearer 123',
- ]);
+ // Arrange
+ $job = $this->dispatch(new FailingJobWithMaxTries);
- $this->expectException(CloudTasksException::class);
- $this->expectExceptionMessage('Could not decode incoming task');
+ // Act & Assert
+ $this->assertDatabaseCount('failed_jobs', 0);
- $this->handler->handle();
+ $releasedJob = $job->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
- // @todo - test with a valid token, not sure how to do that right now
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob->run();
+ $this->assertDatabaseCount('failed_jobs', 1);
}
- /** @test */
- public function it_will_validate_the_token_iss()
+ #[Test]
+ public function uses_worker_options_callback_and_after_max_attempts_it_will_log_to_failed_table()
{
- $this->jwt->shouldReceive('decode')->andReturn((object) [
- 'iss' => 'test',
- ]);
- $this->expectException(CloudTasksException::class);
- $this->expectExceptionMessage('The given OpenID token is not valid');
- $this->handler->handle($this->simpleJob());
+ // Arrange
+ CloudTasksQueue::configureWorkerOptionsUsing(function (IncomingTask $task) {
+ $queueTries = [
+ 'high' => 5,
+ 'low' => 1,
+ ];
+
+ return new WorkerOptions(maxTries: $queueTries[$task->queue()] ?? 1);
+ });
+
+ $job = $this->dispatch(tap(new FailingJobWithNoMaxTries, fn ($job) => $job->queue = 'high'));
+
+ // Act & Assert
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob = $job->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob->run();
+ $this->assertDatabaseCount('failed_jobs', 1);
}
- /** @test */
- public function it_will_validate_the_token_handler()
+ #[Test]
+ public function after_max_attempts_it_will_no_longer_execute_the_task()
{
- $this->jwt->shouldReceive('decode')->andReturn((object) [
- 'iss' => 'accounts.google.com',
- 'aud' => '__incorrect_aud__'
- ]);
- $this->expectException(CloudTasksException::class);
- $this->expectExceptionMessage('The given OpenID token is not valid');
- $this->handler->handle($this->simpleJob());
+ // Arrange
+ Event::fake([JobOutput::class]);
+ $job = $this->dispatch(new FailingJob);
+
+ // Act & Assert
+ $releasedJob = $job->runAndGetReleasedJob();
+ Event::assertDispatched(JobOutput::class, 1);
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob = $releasedJob->runAndGetReleasedJob();
+ Event::assertDispatched(JobOutput::class, 2);
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ $releasedJob->run();
+ Event::assertDispatched(JobOutput::class, 4);
+ $this->assertDatabaseCount('failed_jobs', 1);
}
- /** @test */
- public function it_will_validate_the_token_expiration()
+ #[Test]
+ #[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:00:00', 'should_fail' => false]])]
+ #[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:04:59', 'should_fail' => false]])]
+ #[TestWith([['now' => '2020-01-01 00:00:00', 'try_at' => '2020-01-01 00:05:00', 'should_fail' => true]])]
+ public function after_max_retry_until_it_will_log_to_failed_table(array $args)
{
- $this->jwt->shouldReceive('decode')->andReturn((object) [
- 'iss' => 'accounts.google.com',
- 'aud' => 'https://localhost/my-handler',
- 'exp' => time() - 1
- ]);
- $this->expectException(CloudTasksException::class);
- $this->expectExceptionMessage('The given OpenID token has expired');
- $this->handler->handle($this->simpleJob());
+ // Arrange
+ $this->travelTo($args['now']);
+
+ $job = $this->dispatch(new FailingJobWithRetryUntil);
+
+ // Act
+ $releasedJob = $job->runAndGetReleasedJob();
+
+ // Assert
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ // Act
+ $this->travelTo($args['try_at']);
+ $releasedJob->run();
+
+ // Assert
+ $this->assertDatabaseCount('failed_jobs', $args['should_fail'] ? 1 : 0);
}
- /** @test */
- public function it_runs_the_incoming_job()
+ #[Test]
+ public function test_unlimited_max_attempts()
{
- Mail::fake();
+ // Assert
+ Event::fake(JobOutput::class);
- request()->headers->add(['Authorization' => 'Bearer 123']);
+ // Act
+ $job = $this->dispatch(new FailingJobWithUnlimitedTries);
- $this->handler->handle($this->simpleJob());
+ foreach (range(0, 50) as $attempt) {
+ usleep(1000);
+ $job = $job->runAndGetReleasedJob();
+ }
- Mail::assertSent(TestMailable::class);
+ Event::assertDispatched(JobOutput::class, 51);
}
- private function simpleJob()
+ #[Test]
+ public function test_max_attempts_in_combination_with_retry_until()
{
- return json_decode(file_get_contents(__DIR__ . '/Support/test-job-payload.json'), true);
+ // Arrange
+ $this->travelTo('2020-01-01 00:00:00');
+
+ $job = $this->dispatch(new FailingJobWithMaxTriesAndRetryUntil);
+
+ // When retryUntil is specified, the maxAttempts is ignored.
+
+ // Act & Assert
+
+ // The max attempts is 3, but the retryUntil is set to 5 minutes from now.
+ // So when we attempt the job 4 times, it should still not fail.
+ $job = $job
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob();
+
+ $this->assertDatabaseCount('failed_jobs', 0);
+
+ // Now we travel to 5 minutes from now, and the job should fail.
+ $this->travelTo('2020-01-01 00:05:00');
+ $job->run();
+ $this->assertDatabaseCount('failed_jobs', 1);
+ }
+
+ #[Test]
+ public function it_can_handle_encrypted_jobs()
+ {
+ // Arrange
+ Event::fake(JobOutput::class);
+
+ // Act
+ $job = $this->dispatch(new EncryptedJob);
+ $job->run();
+
+ // Assert
+ Event::assertDispatched(fn (JobOutput $event) => $event->output === 'EncryptedJob:success');
+ }
+
+ #[Test]
+ public function failing_jobs_are_released()
+ {
+ // Arrange
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act
+ $job = $this->dispatch(new FailingJob);
+
+ CloudTasksApi::assertDeletedTaskCount(0);
+ CloudTasksApi::assertCreatedTaskCount(1);
+ CloudTasksApi::assertTaskNotDeleted($job->task->getName());
+
+ $job->run();
+
+ CloudTasksApi::assertCreatedTaskCount(2);
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
+ return $event->job->attempts() === 1;
+ });
+ }
+
+ #[Test]
+ public function attempts_are_tracked_internally()
+ {
+ // Arrange
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act & Assert
+ $job = $this->dispatch(new FailingJob);
+
+ $released = $job->runAndGetReleasedJob();
+
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) use (&$releasedJob) {
+ $releasedJob = $event->job->getRawBody();
+
+ return $event->job->attempts() === 1;
+ });
+
+ $released->run();
+
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
+ return $event->job->attempts() === 2;
+ });
+ }
+
+ #[Test]
+ public function retried_jobs_get_a_new_name()
+ {
+ // Arrange
+ Event::fake(JobReleasedAfterException::class);
+ CloudTasksApi::fake();
+
+ // Act & Assert
+ $this->assertCount(0, $this->createdTasks);
+ $this->dispatch(new FailingJob)->runAndGetReleasedJob();
+ $this->assertCount(2, $this->createdTasks);
+ $this->assertNotEquals($this->createdTasks[0]->getName(), $this->createdTasks[1]->getName());
+ }
+
+ #[Test]
+ public function test_job_timeout()
+ {
+ // Arrange
+ Event::fake(JobReleasedAfterException::class);
+
+ // Act
+ $this->dispatch(new SimpleJobWithTimeout)->run();
+
+ // Assert
+ Event::assertDispatched(JobReleasedAfterException::class);
}
}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index c474213..5f10b7f 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -1,48 +1,137 @@
createdTasks[] = $event->task;
+ });
+ }
+
protected function getPackageProviders($app)
{
return [
- \Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksServiceProvider::class,
+ CloudTasksServiceProvider::class,
];
}
- /**
- * Define environment setup.
- *
- * @param \Illuminate\Foundation\Application $app
- * @return void
- */
+ protected function defineDatabaseMigrations()
+ {
+ // Necessary to test the [failed_jobs] table.
+
+ $this->loadMigrationsFrom(__DIR__.'/../vendor/orchestra/testbench-core/laravel/migrations');
+ $this->loadMigrationsFrom(__DIR__.'/../vendor/orchestra/testbench-core/laravel/migrations/queue');
+ }
+
protected function getEnvironmentSetUp($app)
{
- $app['config']->set('queue.default', 'cloudtasks');
- $app['config']->set('queue.connections.cloudtasks', [
+ $app['config']->set('database.default', 'testbench');
+ $app['config']->set('queue.batching.database', 'testbench');
+ $port = env('DB_DRIVER') === 'mysql' ? 3307 : 5432;
+ $app['config']->set('database.connections.testbench', [
+ 'driver' => env('DB_DRIVER', 'mysql'),
+ 'host' => env('DB_HOST', 'mysql'),
+ 'port' => env('DB_PORT', $port),
+ 'database' => 'cloudtasks',
+ 'username' => 'cloudtasks',
+ 'password' => 'cloudtasks',
+ 'prefix' => '',
+ ]);
+
+ $app['config']->set('cache.default', 'file');
+ $app['config']->set('queue.default', 'my-cloudtasks-connection');
+ $app['config']->set('queue.connections.my-cloudtasks-connection', [
'driver' => 'cloudtasks',
- 'credentials' => __DIR__ . '/Support/gcloud-key-valid.json',
- 'queue' => 'test-queue',
- 'project' => 'test-project',
+ 'queue' => 'barbequeue',
+ 'project' => 'my-test-project',
'location' => 'europe-west6',
- 'handler' => 'https://localhost/my-handler',
+ 'handler' => env('CLOUD_TASKS_HANDLER', 'https://docker.for.mac.localhost:8080/'),
'service_account_email' => 'info@stackkit.io',
]);
+
+ $app['config']->set('queue.connections.my-other-cloudtasks-connection', [
+ ...config('queue.connections.my-cloudtasks-connection'),
+ 'queue' => 'other-barbequeue',
+ 'project' => 'other-my-test-project',
+ ]);
+
+ $app['config']->set('queue.failed.driver', 'database-uuids');
+ $app['config']->set('queue.failed.database', 'testbench');
}
protected function setConfigValue($key, $value)
{
- $this->app['config']->set('queue.connections.cloudtasks.' . $key, $value);
+ $this->app['config']->set('queue.connections.my-cloudtasks-connection.'.$key, $value);
+ }
+
+ public function dispatch($job, ?string $onQueue = null): DispatchedJob
+ {
+ $payload = null;
+ $task = null;
+
+ Event::listen(TaskCreated::class, function (TaskCreated $event) use (&$payload, &$task) {
+ $request = $event->task->getHttpRequest() ?? $event->task->getAppEngineHttpRequest();
+ $payload = $request->getBody();
+ $task = $event->task;
+ });
+
+ if ($job instanceof PendingBatch) {
+ if ($onQueue) {
+ $job->onQueue($onQueue);
+ }
+
+ $job->dispatch();
+ } else {
+ $pendingDispatch = dispatch($job);
+
+ if ($onQueue) {
+ $pendingDispatch->onQueue($onQueue);
+ }
+
+ unset($pendingDispatch);
+ }
+
+ return new DispatchedJob($payload, $task, $this);
+ }
+
+ public function withTaskType(string $taskType): void
+ {
+ switch ($taskType) {
+ case 'appengine':
+ $this->setConfigValue('handler', null);
+ $this->setConfigValue('service_account_email', null);
+
+ $this->setConfigValue('app_engine', true);
+ $this->setConfigValue('app_engine_service', 'api');
+ break;
+ case 'http':
+ $this->setConfigValue('app_engine', false);
+ $this->setConfigValue('app_engine_service', null);
+
+ $this->setConfigValue('handler', 'https://docker.for.mac.localhost:8080/');
+ $this->setConfigValue('service_account_email', 'info@stackkit.io');
+ break;
+ }
}
}