diff --git a/.env b/.env deleted file mode 100644 index 9e19457..0000000 --- a/.env +++ /dev/null @@ -1,27 +0,0 @@ -# In all environments, the following files are loaded if they exist, -# the latter taking precedence over the former: -# -# * .env contains default values for the environment variables needed by the app -# * .env.local uncommitted file with local overrides -# * .env.$APP_ENV committed environment-specific defaults -# * .env.$APP_ENV.local uncommitted environment-specific overrides -# -# Real environment variables win over .env files. -# -# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. -# https://symfony.com/doc/current/configuration/secrets.html -# -# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). -# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration - -###> symfony/framework-bundle ### -APP_ENV=dev -APP_SECRET= -APP_SHARE_DIR=var/share -###< symfony/framework-bundle ### - -###> symfony/routing ### -# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. -# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands -DEFAULT_URI=http://localhost -###< symfony/routing ### diff --git a/.env.dev b/.env.dev deleted file mode 100644 index 9342843..0000000 --- a/.env.dev +++ /dev/null @@ -1,4 +0,0 @@ - -###> symfony/framework-bundle ### -APP_SECRET=b9f97dbf63662a281f041195fe989f8e -###< symfony/framework-bundle ### diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3c53b55 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# Database Configuration +DB_PATH=db/database.sqlite + +# CORS Configuration +CORS_ALLOW_ORIGIN=* +CORS_ALLOW_METHODS="GET, POST, PATCH, DELETE, OPTIONS" +CORS_ALLOW_HEADERS=Content-Type + +# Application Configuration +ERROR_REPORTING=true diff --git a/.gitignore b/.gitignore index a67f91e..6a0dbd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ +# Environment variables +.env -###> symfony/framework-bundle ### -/.env.local -/.env.local.php -/.env.*.local -/config/secrets/prod/prod.decrypt.private.php -/public/bundles/ -/var/ -/vendor/ -###< symfony/framework-bundle ### +# Database +/db/database.sqlite diff --git a/README.md b/README.md index 42a9b61..f614201 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,161 @@ -# listthingy-api +# list thingy API -A list API based off of Symfony for shopping, todos, or other lists \ No newline at end of file +A list thingy API using plain PHP for shopping, todos, or other lists. Users create a list instantly without any signup. +They will get a UUID and can share it with friends and collaborate on lists. This API will manage the list with API +calls and will also run a websocket the iOS and Android apps can connect to. + +- No accounts +- No ads +- No bloat +- Code that just works for 15 years + +## API + +#### Lists + +- POST /list +- GET /list/{uuid} +- PATCH /list/{uuid} +- DELETE /list/{uuid} + +#### Items + +- POST /list/{uuid}/item +- PATCH /list/{uuid}/item/{id} +- DELETE /list/{uuid}/item/{id} + +## Setup + +### Requirements + +- PHP 8.0 or higher +- SQLite3 extension (usually included with PHP) + +### Installation + +1. Clone the repository +2. Copy `.env.example` to `.env`: + +```bash +cp .env.example .env +``` + +3. (Optional) Edit `.env` to customize configuration +4. Start the PHP development server: + +```bash +cd public +php -S localhost:8000 +``` + +The database will be automatically created on first request at `db/database.sqlite`. + +### Configuration + +All configuration is managed through the `.env` file: + +- `DB_PATH` - Path to SQLite database file (relative or absolute) +- `CORS_ALLOW_ORIGIN` - CORS allowed origins (default: `*`) +- `CORS_ALLOW_METHODS` - CORS allowed HTTP methods +- `CORS_ALLOW_HEADERS` - CORS allowed headers +- `ERROR_REPORTING` - Enable/disable error reporting (true/false) + +## Usage Examples + +### Create a List + +```bash +curl -X POST http://localhost:8000/list \ + -H "Content-Type: application/json" \ + -d '{"name":"Shopping List","sharable":true}' +``` + +Response: + +```json +{ + "success": true, + "data": { + "id": 1, + "uuid": "a1b2c3d4e5f6...", + "name": "Shopping List", + "sharable": true, + "created_at": "2025-12-12 12:00:00" + } +} +``` + +### Get a List with Items + +```bash +curl http://localhost:8000/list/{uuid} +``` + +### Update a List + +```bash +curl -X PATCH http://localhost:8000/list/{uuid} \ + -H "Content-Type: application/json" \ + -d '{"name":"Updated List Name"}' +``` + +### Delete a List + +```bash +curl -X DELETE http://localhost:8000/list/{uuid} +``` + +### Add Item to List + +```bash +curl -X POST http://localhost:8000/list/{uuid}/item \ + -H "Content-Type: application/json" \ + -d '{"name":"Milk","quantity":2.5,"category":"Dairy"}' +``` + +### Update Item + +```bash +curl -X PATCH http://localhost:8000/list/{uuid}/item/{id} \ + -H "Content-Type: application/json" \ + -d '{"quantity":3}' +``` + +### Delete Item (Soft Delete) + +```bash +curl -X DELETE http://localhost:8000/list/{uuid}/item/{id} +``` + +## Models + +#### List Model + +- id +- uuid +- name +- sharable (boolean) +- created_at + +#### Item Model + +- id +- list_id +- category +- quantity (double) +- name +- created_at +- deleted_at + +## Architecture + +Pure PHP with no dependencies: + +- **Database**: SQLite with PDO +- **Router**: Custom lightweight router +- **Structure**: Simple MVC pattern +- **CORS**: Enabled for mobile/web apps + +## WebSocket Server + +WebSocket support will be implemented separately for real-time collaboration features. \ No newline at end of file diff --git a/bin/console b/bin/console deleted file mode 100755 index d8d530e..0000000 --- a/bin/console +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env php -=8.4", - "ext-ctype": "*", - "ext-iconv": "*", - "symfony/console": "8.0.*", - "symfony/dotenv": "8.0.*", - "symfony/flex": "^2", - "symfony/framework-bundle": "8.0.*", - "symfony/runtime": "8.0.*", - "symfony/yaml": "8.0.*" - }, - "require-dev": { - }, - "config": { - "allow-plugins": { - "php-http/discovery": true, - "symfony/flex": true, - "symfony/runtime": true - }, - "bump-after-update": true, - "sort-packages": true - }, - "autoload": { - "psr-4": { - "App\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "App\\Tests\\": "tests/" - } - }, - "replace": { - "symfony/polyfill-ctype": "*", - "symfony/polyfill-iconv": "*", - "symfony/polyfill-php72": "*", - "symfony/polyfill-php73": "*", - "symfony/polyfill-php74": "*", - "symfony/polyfill-php80": "*", - "symfony/polyfill-php81": "*", - "symfony/polyfill-php82": "*", - "symfony/polyfill-php83": "*", - "symfony/polyfill-php84": "*" - }, - "scripts": { - "auto-scripts": { - "cache:clear": "symfony-cmd", - "assets:install %PUBLIC_DIR%": "symfony-cmd" - }, - "post-install-cmd": [ - "@auto-scripts" - ], - "post-update-cmd": [ - "@auto-scripts" - ] - }, - "conflict": { - "symfony/symfony": "*" - }, - "extra": { - "symfony": { - "allow-contrib": false, - "require": "8.0.*" - } - } -} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 8666d0c..0000000 --- a/composer.lock +++ /dev/null @@ -1,2482 +0,0 @@ -{ - "_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": "87b6f6d43e0d002cd078c6381dfe1ec9", - "packages": [ - { - "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/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/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": "symfony/cache", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "0e67dc8145810d4e1c0d13c0e1d29ceb930b1c8e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/0e67dc8145810d4e1c0d13c0e1d29ceb930b1c8e", - "reference": "0e67dc8145810d4e1c0d13c0e1d29ceb930b1c8e", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "psr/cache": "^2.0|^3.0", - "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^3.6", - "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^7.4|^8.0" - }, - "conflict": { - "doctrine/dbal": "<4.3", - "ext-redis": "<6.1", - "ext-relay": "<0.12.1" - }, - "provide": { - "psr/cache-implementation": "2.0|3.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0", - "symfony/cache-implementation": "1.1|2.0|3.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/dbal": "^4.3", - "predis/predis": "^1.1|^2.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^7.4|^8.0", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/filesystem": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Cache\\": "" - }, - "classmap": [ - "Traits/ValueWrapper.php" - ], - "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 extended PSR-6, PSR-16 (and tags) implementations", - "homepage": "https://symfony.com", - "keywords": [ - "caching", - "psr6" - ], - "support": { - "source": "https://github.com/symfony/cache/tree/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-04T18:17:06+00:00" - }, - { - "name": "symfony/cache-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/cache": "^3.0" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Cache\\": "" - } - }, - "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 caching", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.6.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": "2025-03-13T15:25:07+00:00" - }, - { - "name": "symfony/config", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "a5a054e613da565d46183a845ae4c0c996a3fbce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/a5a054e613da565d46183a845ae4c0c996a3fbce", - "reference": "a5a054e613da565d46183a845ae4c0c996a3fbce", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.4|^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/service-contracts": "<2.5" - }, - "require-dev": { - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/finder": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "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 you find, load, combine, autofill and validate configuration values of any kind", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/config/tree/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-05T14:08:45+00:00" - }, - { - "name": "symfony/console", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "fcb73f69d655b48fcb894a262f074218df08bd58" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fcb73f69d655b48fcb894a262f074218df08bd58", - "reference": "fcb73f69d655b48fcb894a262f074218df08bd58", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4|^8.0" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.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/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-05T15:25:33+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v8.0.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "90f6c3364b8f444f85bdb6939664c80af9e0d576" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/90f6c3364b8f444f85bdb6939664c80af9e0d576", - "reference": "90f6c3364b8f444f85bdb6939664c80af9e0d576", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.6", - "symfony/var-exporter": "^7.4|^8.0" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "provide": { - "psr/container-implementation": "1.1|2.0", - "symfony/service-implementation": "1.1|2.0|3.0" - }, - "require-dev": { - "symfony/config": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/yaml": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "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 you to standardize and centralize the way objects are constructed in your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v8.0.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-08T06:57:48+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-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.6.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/dotenv", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "460b4067a85288c59a59ce8c1bfb3942e71fd85c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/460b4067a85288c59a59ce8c1bfb3942e71fd85c", - "reference": "460b4067a85288c59a59ce8c1bfb3942e71fd85c", - "shasum": "" - }, - "require": { - "php": ">=8.4" - }, - "require-dev": { - "symfony/console": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Dotenv\\": "" - }, - "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": "Registers environment variables from a .env file", - "homepage": "https://symfony.com", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "support": { - "source": "https://github.com/symfony/dotenv/tree/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-16T10:17:21+00:00" - }, - { - "name": "symfony/error-handler", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "d77ec7dda0c274178745d152e82baf7ea827fd73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/d77ec7dda0c274178745d152e82baf7ea827fd73", - "reference": "d77ec7dda0c274178745d152e82baf7ea827fd73", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "psr/log": "^1|^2|^3", - "symfony/polyfill-php85": "^1.32", - "symfony/var-dumper": "^7.4|^8.0" - }, - "conflict": { - "symfony/deprecation-contracts": "<2.5" - }, - "require-dev": { - "symfony/console": "^7.4|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/webpack-encore-bundle": "^1.0|^2.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/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-05T14:36:47+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "573f95783a2ec6e38752979db139f09fec033f03" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", - "reference": "573f95783a2ec6e38752979db139f09fec033f03", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/security-http": "<7.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": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/error-handler": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/framework-bundle": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^7.4|^8.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/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-10-30T14:17:19+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", - "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.6-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.6.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/filesystem", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" - }, - "require-dev": { - "symfony/process": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-01T09:13:36+00:00" - }, - { - "name": "symfony/finder", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7598dd5770580fa3517ec83e8da0c9b9e01f4291", - "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291", - "shasum": "" - }, - "require": { - "php": ">=8.4" - }, - "require-dev": { - "symfony/filesystem": "^7.4|^8.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/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-05T14:36:47+00:00" - }, - { - "name": "symfony/flex", - "version": "v2.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/flex.git", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.1", - "php": ">=8.1" - }, - "conflict": { - "composer/semver": "<1.7.2", - "symfony/dotenv": "<5.4" - }, - "require-dev": { - "composer/composer": "^2.1", - "symfony/dotenv": "^6.4|^7.4|^8.0", - "symfony/filesystem": "^6.4|^7.4|^8.0", - "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", - "symfony/process": "^6.4|^7.4|^8.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Symfony\\Flex\\Flex" - }, - "autoload": { - "psr-4": { - "Symfony\\Flex\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien.potencier@gmail.com" - } - ], - "description": "Composer plugin for Symfony", - "support": { - "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.10.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-16T09:38:19+00:00" - }, - { - "name": "symfony/framework-bundle", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/framework-bundle.git", - "reference": "194b5edd7a00703aaf836b56bbf079cd6c4d4a87" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/194b5edd7a00703aaf836b56bbf079cd6c4d4a87", - "reference": "194b5edd7a00703aaf836b56bbf079cd6c4d4a87", - "shasum": "" - }, - "require": { - "composer-runtime-api": ">=2.1", - "ext-xml": "*", - "php": ">=8.4", - "symfony/cache": "^7.4|^8.0", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/filesystem": "^7.4|^8.0", - "symfony/finder": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php85": "^1.32", - "symfony/routing": "^7.4|^8.0" - }, - "conflict": { - "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<7.4", - "symfony/form": "<7.4", - "symfony/json-streamer": "<7.4", - "symfony/messenger": "<7.4", - "symfony/security-csrf": "<7.4", - "symfony/serializer": "<7.4", - "symfony/translation": "<7.4", - "symfony/webhook": "<7.4", - "symfony/workflow": "<7.4" - }, - "require-dev": { - "doctrine/persistence": "^1.3|^2|^3", - "dragonmantank/cron-expression": "^3.1", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "phpstan/phpdoc-parser": "^1.0|^2.0", - "seld/jsonlint": "^1.10", - "symfony/asset": "^7.4|^8.0", - "symfony/asset-mapper": "^7.4|^8.0", - "symfony/browser-kit": "^7.4|^8.0", - "symfony/clock": "^7.4|^8.0", - "symfony/console": "^7.4|^8.0", - "symfony/css-selector": "^7.4|^8.0", - "symfony/dom-crawler": "^7.4|^8.0", - "symfony/dotenv": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/form": "^7.4|^8.0", - "symfony/html-sanitizer": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/json-streamer": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/mailer": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", - "symfony/notifier": "^7.4|^8.0", - "symfony/object-mapper": "^7.4|^8.0", - "symfony/polyfill-intl-icu": "^1.0", - "symfony/process": "^7.4|^8.0", - "symfony/property-info": "^7.4|^8.0", - "symfony/rate-limiter": "^7.4|^8.0", - "symfony/runtime": "^7.4|^8.0", - "symfony/scheduler": "^7.4|^8.0", - "symfony/security-bundle": "^7.4|^8.0", - "symfony/semaphore": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/string": "^7.4|^8.0", - "symfony/translation": "^7.4|^8.0", - "symfony/twig-bundle": "^7.4|^8.0", - "symfony/type-info": "^7.4.1|^8.0.1", - "symfony/uid": "^7.4|^8.0", - "symfony/validator": "^7.4|^8.0", - "symfony/web-link": "^7.4|^8.0", - "symfony/webhook": "^7.4|^8.0", - "symfony/workflow": "^7.4|^8.0", - "symfony/yaml": "^7.4|^8.0" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\FrameworkBundle\\": "" - }, - "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 tight integration between Symfony components and the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-06T16:55:34+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "3690740e2e8b19d877f20d4f10b7a489cddf0fe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3690740e2e8b19d877f20d4f10b7a489cddf0fe2", - "reference": "3690740e2e8b19d877f20d4f10b7a489cddf0fe2", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.1" - }, - "conflict": { - "doctrine/dbal": "<4.3" - }, - "require-dev": { - "doctrine/dbal": "^4.3", - "predis/predis": "^1.1|^2.0", - "symfony/cache": "^7.4|^8.0", - "symfony/clock": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", - "symfony/rate-limiter": "^7.4|^8.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/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-07T11:23:24+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v8.0.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "bcef77a3c8ae8934ce7067172e2a1a6491a62a7d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/bcef77a3c8ae8934ce7067172e2a1a6491a62a7d", - "reference": "bcef77a3c8ae8934ce7067172e2a1a6491a62a7d", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "psr/log": "^1|^2|^3", - "symfony/error-handler": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/flex": "<2.10", - "symfony/http-client-contracts": "<2.5", - "symfony/translation-contracts": "<2.5", - "twig/twig": "<3.21" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^7.4|^8.0", - "symfony/clock": "^7.4|^8.0", - "symfony/config": "^7.4|^8.0", - "symfony/console": "^7.4|^8.0", - "symfony/css-selector": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/dom-crawler": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/finder": "^7.4|^8.0", - "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^7.4|^8.0", - "symfony/property-access": "^7.4|^8.0", - "symfony/routing": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/translation": "^7.4|^8.0", - "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^7.4|^8.0", - "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0", - "symfony/var-exporter": "^7.4|^8.0", - "twig/twig": "^3.21" - }, - "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/v8.0.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-08T07:59:34+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-27T09:58:17+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "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.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "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.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/polyfill-php85", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "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\\Php85\\": "" - }, - "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.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-23T16:12:55+00:00" - }, - { - "name": "symfony/routing", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "bc8fa314a61fb7c4190e964b18a5bd000d3b45ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/bc8fa314a61fb7c4190e964b18a5bd000d3b45ce", - "reference": "bc8fa314a61fb7c4190e964b18a5bd000d3b45ce", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/yaml": "^7.4|^8.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/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-01T09:13:36+00:00" - }, - { - "name": "symfony/runtime", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/runtime.git", - "reference": "73b34037b23db051048ba2873031ddb89be9f19d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/73b34037b23db051048ba2873031ddb89be9f19d", - "reference": "73b34037b23db051048ba2873031ddb89be9f19d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=8.4" - }, - "conflict": { - "symfony/error-handler": "<7.4" - }, - "require-dev": { - "composer/composer": "^2.6", - "symfony/console": "^7.4|^8.0", - "symfony/dotenv": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Runtime\\": "", - "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" - }, - "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": "Enables decoupling PHP applications from global state", - "homepage": "https://symfony.com", - "keywords": [ - "runtime" - ], - "support": { - "source": "https://github.com/symfony/runtime/tree/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-05T14:08:45+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", - "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.6-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.6.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T11:30:57+00:00" - }, - { - "name": "symfony/string", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.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/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-01T09:13:36+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "d2a2476c93b58ac5292145e9fac1ff76a21d1ce2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d2a2476c93b58ac5292145e9fac1ff76a21d1ce2", - "reference": "d2a2476c93b58ac5292145e9fac1ff76a21d1ce2", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "symfony/console": "<7.4", - "symfony/error-handler": "<7.4" - }, - "require-dev": { - "symfony/console": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/uid": "^7.4|^8.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/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-10-28T09:34:19+00:00" - }, - { - "name": "symfony/var-exporter", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "shasum": "" - }, - "require": { - "php": ">=8.4" - }, - "require-dev": { - "symfony/property-access": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "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": "Allows exporting any serializable PHP data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "lazy-loading", - "proxy", - "serialize" - ], - "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-05T18:53:00+00:00" - }, - { - "name": "symfony/yaml", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", - "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<7.4" - }, - "require-dev": { - "symfony/console": "^7.4|^8.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/v8.0.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-04T18:17:06+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": ">=8.4", - "ext-ctype": "*", - "ext-iconv": "*" - }, - "platform-dev": [], - "plugin-api-version": "2.6.0" -} diff --git a/config/bundles.php b/config/bundles.php deleted file mode 100644 index 49d3fb6..0000000 --- a/config/bundles.php +++ /dev/null @@ -1,5 +0,0 @@ - ['all' => true], -]; diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..216148c --- /dev/null +++ b/config/config.php @@ -0,0 +1,30 @@ + [ + 'path' => $dbPath, + ], + 'cors' => [ + 'allow_origin' => Env::get('CORS_ALLOW_ORIGIN', '*'), + 'allow_methods' => Env::get('CORS_ALLOW_METHODS', 'GET, POST, PATCH, DELETE, OPTIONS'), + 'allow_headers' => Env::get('CORS_ALLOW_HEADERS', 'Content-Type'), + ], + 'error_reporting' => Env::getBool('ERROR_REPORTING', true), +]; diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml deleted file mode 100644 index 6899b72..0000000 --- a/config/packages/cache.yaml +++ /dev/null @@ -1,19 +0,0 @@ -framework: - cache: - # Unique name of your app: used to compute stable namespaces for cache keys. - #prefix_seed: your_vendor_name/app_name - - # The "app" cache stores to the filesystem by default. - # The data in this cache should persist between deploys. - # Other options include: - - # Redis - #app: cache.adapter.redis - #default_redis_provider: redis://localhost - - # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) - #app: cache.adapter.apcu - - # Namespaced pools use the above "app" backend by default - #pools: - #my.dedicated.cache: null diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml deleted file mode 100644 index 7e1ee1f..0000000 --- a/config/packages/framework.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# see https://symfony.com/doc/current/reference/configuration/framework.html -framework: - secret: '%env(APP_SECRET)%' - - # Note that the session will be started ONLY if you read or write from it. - session: true - - #esi: true - #fragments: true - -when@test: - framework: - test: true - session: - storage_factory_id: session.storage.factory.mock_file diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml deleted file mode 100644 index 0f34f87..0000000 --- a/config/packages/routing.yaml +++ /dev/null @@ -1,10 +0,0 @@ -framework: - router: - # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. - # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands - default_uri: '%env(DEFAULT_URI)%' - -when@prod: - framework: - router: - strict_requirements: null diff --git a/config/preload.php b/config/preload.php deleted file mode 100644 index 5ebcdb2..0000000 --- a/config/preload.php +++ /dev/null @@ -1,5 +0,0 @@ - [ - * 'App\\' => [ - * 'resource' => '../src/', - * ], - * ], - * ]); - * ``` - * - * @psalm-type ImportsConfig = list - * @psalm-type ParametersConfig = array|null>|null> - * @psalm-type ArgumentsType = list|array - * @psalm-type CallType = array|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool} - * @psalm-type TagsType = list>> // arrays inside the list must have only one element, with the tag name as the key - * @psalm-type CallbackType = string|array{0:string|ReferenceConfigurator,1:string}|\Closure|ReferenceConfigurator|ExpressionConfigurator - * @psalm-type DeprecationType = array{package: string, version: string, message?: string} - * @psalm-type DefaultsType = array{ - * public?: bool, - * tags?: TagsType, - * resource_tags?: TagsType, - * autowire?: bool, - * autoconfigure?: bool, - * bind?: array, - * } - * @psalm-type InstanceofType = array{ - * shared?: bool, - * lazy?: bool|string, - * public?: bool, - * properties?: array, - * configurator?: CallbackType, - * calls?: list, - * tags?: TagsType, - * resource_tags?: TagsType, - * autowire?: bool, - * bind?: array, - * constructor?: string, - * } - * @psalm-type DefinitionType = array{ - * class?: string, - * file?: string, - * parent?: string, - * shared?: bool, - * synthetic?: bool, - * lazy?: bool|string, - * public?: bool, - * abstract?: bool, - * deprecated?: DeprecationType, - * factory?: CallbackType, - * configurator?: CallbackType, - * arguments?: ArgumentsType, - * properties?: array, - * calls?: list, - * tags?: TagsType, - * resource_tags?: TagsType, - * decorates?: string, - * decoration_inner_name?: string, - * decoration_priority?: int, - * decoration_on_invalid?: 'exception'|'ignore'|null, - * autowire?: bool, - * autoconfigure?: bool, - * bind?: array, - * constructor?: string, - * from_callable?: CallbackType, - * } - * @psalm-type AliasType = string|array{ - * alias: string, - * public?: bool, - * deprecated?: DeprecationType, - * } - * @psalm-type PrototypeType = array{ - * resource: string, - * namespace?: string, - * exclude?: string|list, - * parent?: string, - * shared?: bool, - * lazy?: bool|string, - * public?: bool, - * abstract?: bool, - * deprecated?: DeprecationType, - * factory?: CallbackType, - * arguments?: ArgumentsType, - * properties?: array, - * configurator?: CallbackType, - * calls?: list, - * tags?: TagsType, - * resource_tags?: TagsType, - * autowire?: bool, - * autoconfigure?: bool, - * bind?: array, - * constructor?: string, - * } - * @psalm-type StackType = array{ - * stack: list>, - * public?: bool, - * deprecated?: DeprecationType, - * } - * @psalm-type ServicesConfig = array{ - * _defaults?: DefaultsType, - * _instanceof?: InstanceofType, - * ... - * } - * @psalm-type ExtensionType = array - * @psalm-type FrameworkConfig = array{ - * secret?: scalar|null, - * http_method_override?: bool, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false - * allowed_http_method_override?: list|null, - * trust_x_sendfile_type_header?: scalar|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%" - * ide?: scalar|null, // Default: "%env(default::SYMFONY_IDE)%" - * test?: bool, - * default_locale?: scalar|null, // Default: "en" - * set_locale_from_accept_language?: bool, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false - * set_content_language_from_locale?: bool, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false - * enabled_locales?: list, - * trusted_hosts?: list, - * trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"] - * trusted_headers?: list, - * error_controller?: scalar|null, // Default: "error_controller" - * handle_all_throwables?: bool, // HttpKernel will handle all kinds of \Throwable. // Default: true - * csrf_protection?: bool|array{ - * enabled?: scalar|null, // Default: null - * stateless_token_ids?: list, - * check_header?: scalar|null, // Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. // Default: false - * cookie_name?: scalar|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token" - * }, - * form?: bool|array{ // Form configuration - * enabled?: bool, // Default: false - * csrf_protection?: array{ - * enabled?: scalar|null, // Default: null - * token_id?: scalar|null, // Default: null - * field_name?: scalar|null, // Default: "_token" - * field_attr?: array, - * }, - * }, - * http_cache?: bool|array{ // HTTP cache configuration - * enabled?: bool, // Default: false - * debug?: bool, // Default: "%kernel.debug%" - * trace_level?: "none"|"short"|"full", - * trace_header?: scalar|null, - * default_ttl?: int, - * private_headers?: list, - * skip_response_headers?: list, - * allow_reload?: bool, - * allow_revalidate?: bool, - * stale_while_revalidate?: int, - * stale_if_error?: int, - * terminate_on_cache_hit?: bool, - * }, - * esi?: bool|array{ // ESI configuration - * enabled?: bool, // Default: false - * }, - * ssi?: bool|array{ // SSI configuration - * enabled?: bool, // Default: false - * }, - * fragments?: bool|array{ // Fragments configuration - * enabled?: bool, // Default: false - * hinclude_default_template?: scalar|null, // Default: null - * path?: scalar|null, // Default: "/_fragment" - * }, - * profiler?: bool|array{ // Profiler configuration - * enabled?: bool, // Default: false - * collect?: bool, // Default: true - * collect_parameter?: scalar|null, // The name of the parameter to use to enable or disable collection on a per request basis. // Default: null - * only_exceptions?: bool, // Default: false - * only_main_requests?: bool, // Default: false - * dsn?: scalar|null, // Default: "file:%kernel.cache_dir%/profiler" - * collect_serializer_data?: true, // Default: true - * }, - * workflows?: bool|array{ - * enabled?: bool, // Default: false - * workflows?: array, - * definition_validators?: list, - * support_strategy?: scalar|null, - * initial_marking?: list, - * events_to_dispatch?: list|null, - * places?: list, - * }>, - * transitions: list, - * to?: list, - * weight?: int, // Default: 1 - * metadata?: list, - * }>, - * metadata?: list, - * }>, - * }, - * router?: bool|array{ // Router configuration - * enabled?: bool, // Default: false - * resource: scalar|null, - * type?: scalar|null, - * default_uri?: scalar|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null - * http_port?: scalar|null, // Default: 80 - * https_port?: scalar|null, // Default: 443 - * strict_requirements?: scalar|null, // set to true to throw an exception when a parameter does not match the requirements set to false to disable exceptions when a parameter does not match the requirements (and return null instead) set to null to disable parameter checks against requirements 'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production // Default: true - * utf8?: bool, // Default: true - * }, - * session?: bool|array{ // Session configuration - * enabled?: bool, // Default: false - * storage_factory_id?: scalar|null, // Default: "session.storage.factory.native" - * handler_id?: scalar|null, // Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null. - * name?: scalar|null, - * cookie_lifetime?: scalar|null, - * cookie_path?: scalar|null, - * cookie_domain?: scalar|null, - * cookie_secure?: true|false|"auto", // Default: "auto" - * cookie_httponly?: bool, // Default: true - * cookie_samesite?: null|"lax"|"strict"|"none", // Default: "lax" - * use_cookies?: bool, - * gc_divisor?: scalar|null, - * gc_probability?: scalar|null, - * gc_maxlifetime?: scalar|null, - * save_path?: scalar|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null. - * metadata_update_threshold?: int, // Seconds to wait between 2 session metadata updates. // Default: 0 - * }, - * request?: bool|array{ // Request configuration - * enabled?: bool, // Default: false - * formats?: array>, - * }, - * assets?: bool|array{ // Assets configuration - * enabled?: bool, // Default: false - * strict_mode?: bool, // Throw an exception if an entry is missing from the manifest.json. // Default: false - * version_strategy?: scalar|null, // Default: null - * version?: scalar|null, // Default: null - * version_format?: scalar|null, // Default: "%%s?%%s" - * json_manifest_path?: scalar|null, // Default: null - * base_path?: scalar|null, // Default: "" - * base_urls?: list, - * packages?: array, - * }>, - * }, - * asset_mapper?: bool|array{ // Asset Mapper configuration - * enabled?: bool, // Default: false - * paths?: array, - * excluded_patterns?: list, - * exclude_dotfiles?: bool, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true - * server?: bool, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true - * public_prefix?: scalar|null, // The public path where the assets will be written to (and served from when "server" is true). // Default: "/assets/" - * missing_import_mode?: "strict"|"warn"|"ignore", // Behavior if an asset cannot be found when imported from JavaScript or CSS files - e.g. "import './non-existent.js'". "strict" means an exception is thrown, "warn" means a warning is logged, "ignore" means the import is left as-is. // Default: "warn" - * extensions?: array, - * importmap_path?: scalar|null, // The path of the importmap.php file. // Default: "%kernel.project_dir%/importmap.php" - * importmap_polyfill?: scalar|null, // The importmap name that will be used to load the polyfill. Set to false to disable. // Default: "es-module-shims" - * importmap_script_attributes?: array, - * vendor_dir?: scalar|null, // The directory to store JavaScript vendors. // Default: "%kernel.project_dir%/assets/vendor" - * precompress?: bool|array{ // Precompress assets with Brotli, Zstandard and gzip. - * enabled?: bool, // Default: false - * formats?: list, - * extensions?: list, - * }, - * }, - * translator?: bool|array{ // Translator configuration - * enabled?: bool, // Default: false - * fallbacks?: list, - * logging?: bool, // Default: false - * formatter?: scalar|null, // Default: "translator.formatter.default" - * cache_dir?: scalar|null, // Default: "%kernel.cache_dir%/translations" - * default_path?: scalar|null, // The default path used to load translations. // Default: "%kernel.project_dir%/translations" - * paths?: list, - * pseudo_localization?: bool|array{ - * enabled?: bool, // Default: false - * accents?: bool, // Default: true - * expansion_factor?: float, // Default: 1.0 - * brackets?: bool, // Default: true - * parse_html?: bool, // Default: false - * localizable_html_attributes?: list, - * }, - * providers?: array, - * locales?: list, - * }>, - * globals?: array, - * domain?: string, - * }>, - * }, - * validation?: bool|array{ // Validation configuration - * enabled?: bool, // Default: false - * enable_attributes?: bool, // Default: true - * static_method?: list, - * translation_domain?: scalar|null, // Default: "validators" - * email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict", // Default: "html5" - * mapping?: array{ - * paths?: list, - * }, - * not_compromised_password?: bool|array{ - * enabled?: bool, // When disabled, compromised passwords will be accepted as valid. // Default: true - * endpoint?: scalar|null, // API endpoint for the NotCompromisedPassword Validator. // Default: null - * }, - * disable_translation?: bool, // Default: false - * auto_mapping?: array, - * }>, - * }, - * serializer?: bool|array{ // Serializer configuration - * enabled?: bool, // Default: false - * enable_attributes?: bool, // Default: true - * name_converter?: scalar|null, - * circular_reference_handler?: scalar|null, - * max_depth_handler?: scalar|null, - * mapping?: array{ - * paths?: list, - * }, - * default_context?: list, - * named_serializers?: array, - * include_built_in_normalizers?: bool, // Whether to include the built-in normalizers // Default: true - * include_built_in_encoders?: bool, // Whether to include the built-in encoders // Default: true - * }>, - * }, - * property_access?: bool|array{ // Property access configuration - * enabled?: bool, // Default: false - * magic_call?: bool, // Default: false - * magic_get?: bool, // Default: true - * magic_set?: bool, // Default: true - * throw_exception_on_invalid_index?: bool, // Default: false - * throw_exception_on_invalid_property_path?: bool, // Default: true - * }, - * type_info?: bool|array{ // Type info configuration - * enabled?: bool, // Default: false - * aliases?: array, - * }, - * property_info?: bool|array{ // Property info configuration - * enabled?: bool, // Default: false - * with_constructor_extractor?: bool, // Registers the constructor extractor. // Default: true - * }, - * cache?: array{ // Cache configuration - * prefix_seed?: scalar|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%" - * app?: scalar|null, // App related cache pools configuration. // Default: "cache.adapter.filesystem" - * system?: scalar|null, // System related cache pools configuration. // Default: "cache.adapter.system" - * directory?: scalar|null, // Default: "%kernel.share_dir%/pools/app" - * default_psr6_provider?: scalar|null, - * default_redis_provider?: scalar|null, // Default: "redis://localhost" - * default_valkey_provider?: scalar|null, // Default: "valkey://localhost" - * default_memcached_provider?: scalar|null, // Default: "memcached://localhost" - * default_doctrine_dbal_provider?: scalar|null, // Default: "database_connection" - * default_pdo_provider?: scalar|null, // Default: null - * pools?: array, - * tags?: scalar|null, // Default: null - * public?: bool, // Default: false - * default_lifetime?: scalar|null, // Default lifetime of the pool. - * provider?: scalar|null, // Overwrite the setting from the default provider for this adapter. - * early_expiration_message_bus?: scalar|null, - * clearer?: scalar|null, - * }>, - * }, - * php_errors?: array{ // PHP errors handling configuration - * log?: mixed, // Use the application logger instead of the PHP logger for logging PHP errors. // Default: true - * throw?: bool, // Throw PHP errors as \ErrorException instances. // Default: true - * }, - * exceptions?: array, - * web_link?: bool|array{ // Web links configuration - * enabled?: bool, // Default: false - * }, - * lock?: bool|string|array{ // Lock configuration - * enabled?: bool, // Default: false - * resources?: array>, - * }, - * semaphore?: bool|string|array{ // Semaphore configuration - * enabled?: bool, // Default: false - * resources?: array, - * }, - * messenger?: bool|array{ // Messenger configuration - * enabled?: bool, // Default: false - * routing?: array, - * }>, - * serializer?: array{ - * default_serializer?: scalar|null, // Service id to use as the default serializer for the transports. // Default: "messenger.transport.native_php_serializer" - * symfony_serializer?: array{ - * format?: scalar|null, // Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default). // Default: "json" - * context?: array, - * }, - * }, - * transports?: array, - * failure_transport?: scalar|null, // Transport name to send failed messages to (after all retries have failed). // Default: null - * retry_strategy?: string|array{ - * service?: scalar|null, // Service id to override the retry strategy entirely. // Default: null - * max_retries?: int, // Default: 3 - * delay?: int, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 - * multiplier?: float, // If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)). // Default: 2 - * max_delay?: int, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 - * jitter?: float, // Randomness to apply to the delay (between 0 and 1). // Default: 0.1 - * }, - * rate_limiter?: scalar|null, // Rate limiter name to use when processing messages. // Default: null - * }>, - * failure_transport?: scalar|null, // Transport name to send failed messages to (after all retries have failed). // Default: null - * stop_worker_on_signals?: list, - * default_bus?: scalar|null, // Default: null - * buses?: array, - * }>, - * }>, - * }, - * scheduler?: bool|array{ // Scheduler configuration - * enabled?: bool, // Default: false - * }, - * disallow_search_engine_index?: bool, // Enabled by default when debug is enabled. // Default: true - * http_client?: bool|array{ // HTTP Client configuration - * enabled?: bool, // Default: false - * max_host_connections?: int, // The maximum number of connections to a single host. - * default_options?: array{ - * headers?: array, - * vars?: array, - * max_redirects?: int, // The maximum number of redirects to follow. - * http_version?: scalar|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. - * resolve?: array, - * proxy?: scalar|null, // The URL of the proxy to pass requests through or null for automatic detection. - * no_proxy?: scalar|null, // A comma separated list of hosts that do not require a proxy to be reached. - * timeout?: float, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. - * max_duration?: float, // The maximum execution time for the request+response as a whole. - * bindto?: scalar|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. - * verify_peer?: bool, // Indicates if the peer should be verified in a TLS context. - * verify_host?: bool, // Indicates if the host should exist as a certificate common name. - * cafile?: scalar|null, // A certificate authority file. - * capath?: scalar|null, // A directory that contains multiple certificate authority files. - * local_cert?: scalar|null, // A PEM formatted certificate file. - * local_pk?: scalar|null, // A private key file. - * passphrase?: scalar|null, // The passphrase used to encrypt the "local_pk" file. - * ciphers?: scalar|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...) - * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). - * sha1?: mixed, - * pin-sha256?: mixed, - * md5?: mixed, - * }, - * crypto_method?: scalar|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. - * extra?: array, - * rate_limiter?: scalar|null, // Rate limiter name to use for throttling requests. // Default: null - * caching?: bool|array{ // Caching configuration. - * enabled?: bool, // Default: false - * cache_pool?: string, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" - * shared?: bool, // Indicates whether the cache is shared (public) or private. // Default: true - * max_ttl?: int, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null - * }, - * retry_failed?: bool|array{ - * enabled?: bool, // Default: false - * retry_strategy?: scalar|null, // service id to override the retry strategy. // Default: null - * http_codes?: array, - * }>, - * max_retries?: int, // Default: 3 - * delay?: int, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 - * multiplier?: float, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 - * max_delay?: int, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 - * jitter?: float, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 - * }, - * }, - * mock_response_factory?: scalar|null, // The id of the service that should generate mock responses. It should be either an invokable or an iterable. - * scoped_clients?: array, - * headers?: array, - * max_redirects?: int, // The maximum number of redirects to follow. - * http_version?: scalar|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. - * resolve?: array, - * proxy?: scalar|null, // The URL of the proxy to pass requests through or null for automatic detection. - * no_proxy?: scalar|null, // A comma separated list of hosts that do not require a proxy to be reached. - * timeout?: float, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. - * max_duration?: float, // The maximum execution time for the request+response as a whole. - * bindto?: scalar|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. - * verify_peer?: bool, // Indicates if the peer should be verified in a TLS context. - * verify_host?: bool, // Indicates if the host should exist as a certificate common name. - * cafile?: scalar|null, // A certificate authority file. - * capath?: scalar|null, // A directory that contains multiple certificate authority files. - * local_cert?: scalar|null, // A PEM formatted certificate file. - * local_pk?: scalar|null, // A private key file. - * passphrase?: scalar|null, // The passphrase used to encrypt the "local_pk" file. - * ciphers?: scalar|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...). - * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). - * sha1?: mixed, - * pin-sha256?: mixed, - * md5?: mixed, - * }, - * crypto_method?: scalar|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. - * extra?: array, - * rate_limiter?: scalar|null, // Rate limiter name to use for throttling requests. // Default: null - * caching?: bool|array{ // Caching configuration. - * enabled?: bool, // Default: false - * cache_pool?: string, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" - * shared?: bool, // Indicates whether the cache is shared (public) or private. // Default: true - * max_ttl?: int, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null - * }, - * retry_failed?: bool|array{ - * enabled?: bool, // Default: false - * retry_strategy?: scalar|null, // service id to override the retry strategy. // Default: null - * http_codes?: array, - * }>, - * max_retries?: int, // Default: 3 - * delay?: int, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 - * multiplier?: float, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 - * max_delay?: int, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 - * jitter?: float, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 - * }, - * }>, - * }, - * mailer?: bool|array{ // Mailer configuration - * enabled?: bool, // Default: false - * message_bus?: scalar|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null - * dsn?: scalar|null, // Default: null - * transports?: array, - * envelope?: array{ // Mailer Envelope configuration - * sender?: scalar|null, - * recipients?: list, - * allowed_recipients?: list, - * }, - * headers?: array, - * dkim_signer?: bool|array{ // DKIM signer configuration - * enabled?: bool, // Default: false - * key?: scalar|null, // Key content, or path to key (in PEM format with the `file://` prefix) // Default: "" - * domain?: scalar|null, // Default: "" - * select?: scalar|null, // Default: "" - * passphrase?: scalar|null, // The private key passphrase // Default: "" - * options?: array, - * }, - * smime_signer?: bool|array{ // S/MIME signer configuration - * enabled?: bool, // Default: false - * key?: scalar|null, // Path to key (in PEM format) // Default: "" - * certificate?: scalar|null, // Path to certificate (in PEM format without the `file://` prefix) // Default: "" - * passphrase?: scalar|null, // The private key passphrase // Default: null - * extra_certificates?: scalar|null, // Default: null - * sign_options?: int, // Default: null - * }, - * smime_encrypter?: bool|array{ // S/MIME encrypter configuration - * enabled?: bool, // Default: false - * repository?: scalar|null, // S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`. // Default: "" - * cipher?: int, // A set of algorithms used to encrypt the message // Default: null - * }, - * }, - * secrets?: bool|array{ - * enabled?: bool, // Default: true - * vault_directory?: scalar|null, // Default: "%kernel.project_dir%/config/secrets/%kernel.runtime_environment%" - * local_dotenv_file?: scalar|null, // Default: "%kernel.project_dir%/.env.%kernel.runtime_environment%.local" - * decryption_env_var?: scalar|null, // Default: "base64:default::SYMFONY_DECRYPTION_SECRET" - * }, - * notifier?: bool|array{ // Notifier configuration - * enabled?: bool, // Default: false - * message_bus?: scalar|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null - * chatter_transports?: array, - * texter_transports?: array, - * notification_on_failed_messages?: bool, // Default: false - * channel_policy?: array>, - * admin_recipients?: list, - * }, - * rate_limiter?: bool|array{ // Rate limiter configuration - * enabled?: bool, // Default: false - * limiters?: array, - * limit?: int, // The maximum allowed hits in a fixed interval or burst. - * interval?: scalar|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). - * rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket". - * interval?: scalar|null, // Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). - * amount?: int, // Amount of tokens to add each interval. // Default: 1 - * }, - * }>, - * }, - * uid?: bool|array{ // Uid configuration - * enabled?: bool, // Default: false - * default_uuid_version?: 7|6|4|1, // Default: 7 - * name_based_uuid_version?: 5|3, // Default: 5 - * name_based_uuid_namespace?: scalar|null, - * time_based_uuid_version?: 7|6|1, // Default: 7 - * time_based_uuid_node?: scalar|null, - * }, - * html_sanitizer?: bool|array{ // HtmlSanitizer configuration - * enabled?: bool, // Default: false - * sanitizers?: array, - * block_elements?: list, - * drop_elements?: list, - * allow_attributes?: array, - * drop_attributes?: array, - * force_attributes?: array>, - * force_https_urls?: bool, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false - * allowed_link_schemes?: list, - * allowed_link_hosts?: list|null, - * allow_relative_links?: bool, // Allows relative URLs to be used in links href attributes. // Default: false - * allowed_media_schemes?: list, - * allowed_media_hosts?: list|null, - * allow_relative_medias?: bool, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false - * with_attribute_sanitizers?: list, - * without_attribute_sanitizers?: list, - * max_input_length?: int, // The maximum length allowed for the sanitized input. // Default: 0 - * }>, - * }, - * webhook?: bool|array{ // Webhook configuration - * enabled?: bool, // Default: false - * message_bus?: scalar|null, // The message bus to use. // Default: "messenger.default_bus" - * routing?: array, - * }, - * remote-event?: bool|array{ // RemoteEvent configuration - * enabled?: bool, // Default: false - * }, - * json_streamer?: bool|array{ // JSON streamer configuration - * enabled?: bool, // Default: false - * }, - * } - * @psalm-type ConfigType = array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * framework?: FrameworkConfig, - * "when@dev"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * framework?: FrameworkConfig, - * }, - * "when@prod"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * framework?: FrameworkConfig, - * }, - * "when@test"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * framework?: FrameworkConfig, - * }, - * ..., - * }> - * } - */ -final class App -{ - /** - * @param ConfigType $config - * - * @psalm-return ConfigType - */ - public static function config(array $config): array - { - return AppReference::config($config); - } -} - -namespace Symfony\Component\Routing\Loader\Configurator; - -/** - * This class provides array-shapes for configuring the routes of an application. - * - * Example: - * - * ```php - * // config/routes.php - * namespace Symfony\Component\Routing\Loader\Configurator; - * - * return Routes::config([ - * 'controllers' => [ - * 'resource' => 'routing.controllers', - * ], - * ]); - * ``` - * - * @psalm-type RouteConfig = array{ - * path: string|array, - * controller?: string, - * methods?: string|list, - * requirements?: array, - * defaults?: array, - * options?: array, - * host?: string|array, - * schemes?: string|list, - * condition?: string, - * locale?: string, - * format?: string, - * utf8?: bool, - * stateless?: bool, - * } - * @psalm-type ImportConfig = array{ - * resource: string, - * type?: string, - * exclude?: string|list, - * prefix?: string|array, - * name_prefix?: string, - * trailing_slash_on_root?: bool, - * controller?: string, - * methods?: string|list, - * requirements?: array, - * defaults?: array, - * options?: array, - * host?: string|array, - * schemes?: string|list, - * condition?: string, - * locale?: string, - * format?: string, - * utf8?: bool, - * stateless?: bool, - * } - * @psalm-type AliasConfig = array{ - * alias: string, - * deprecated?: array{package:string, version:string, message?:string}, - * } - * @psalm-type RoutesConfig = array{ - * "when@dev"?: array, - * "when@prod"?: array, - * "when@test"?: array, - * ... - * } - */ -final class Routes -{ - /** - * @param RoutesConfig $config - * - * @psalm-return RoutesConfig - */ - public static function config(array $config): array - { - return $config; - } -} diff --git a/config/routes.yaml b/config/routes.yaml deleted file mode 100644 index cef258c..0000000 --- a/config/routes.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# yaml-language-server: $schema=../vendor/symfony/routing/Loader/schema/routing.schema.json - -# This file is the entry point to configure the routes of your app. -# Methods with the #[Route] attribute are automatically imported. -# See also https://symfony.com/doc/current/routing.html - -# To list all registered routes, run the following command: -# bin/console debug:router - -controllers: - resource: routing.controllers diff --git a/config/routes/framework.yaml b/config/routes/framework.yaml deleted file mode 100644 index bc1feac..0000000 --- a/config/routes/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -when@dev: - _errors: - resource: '@FrameworkBundle/Resources/config/routing/errors.php' - prefix: /_error diff --git a/config/services.yaml b/config/services.yaml deleted file mode 100644 index 79b8ce2..0000000 --- a/config/services.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json - -# This file is the entry point to configure your own services. -# Files in the packages/ subdirectory configure your dependencies. -# See also https://symfony.com/doc/current/service_container/import.html - -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration -parameters: - -services: - # default configuration for services in *this* file - _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. - - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name - App\: - resource: '../src/' - - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones diff --git a/public/index.php b/public/index.php index 9982c21..8c11632 100644 --- a/public/index.php +++ b/public/index.php @@ -1,9 +1,66 @@ register(); -return function (array $context) { - return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); -}; +$config = require __DIR__ . '/../config/config.php'; + +if ($config['error_reporting']) { + error_reporting(E_ALL); + ini_set('display_errors', '1'); +} + +header('Access-Control-Allow-Origin: ' . $config['cors']['allow_origin']); +header('Access-Control-Allow-Methods: ' . $config['cors']['allow_methods']); +header('Access-Control-Allow-Headers: ' . $config['cors']['allow_headers']); + +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(204); + exit; +} + +use App\Database; +use App\Router; +use App\Controllers\ListController; +use App\Controllers\ItemController; + +Database::init($config['db']['path']); + +$router = new Router(); + +$router->addRoute('POST', '/list', function () { + ListController::create(); +}); + +$router->addRoute('GET', '/list/{uuid}', function (string $uuid) { + ListController::show($uuid); +}); + +$router->addRoute('PATCH', '/list/{uuid}', function (string $uuid) { + ListController::update($uuid); +}); + +$router->addRoute('DELETE', '/list/{uuid}', function (string $uuid) { + ListController::delete($uuid); +}); + +$router->addRoute('POST', '/list/{uuid}/item', function (string $uuid) { + ItemController::create($uuid); +}); + +$router->addRoute('PATCH', '/list/{uuid}/item/{id}', function (string $uuid, string $id) { + ItemController::update($uuid, $id); +}); + +$router->addRoute('DELETE', '/list/{uuid}/item/{id}', function (string $uuid, string $id) { + ItemController::delete($uuid, $id); +}); + +try { + $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); +} catch (Exception $e) { + Router::sendResponse(['error' => 'Internal server error'], 500); +} diff --git a/src/Autoloader.php b/src/Autoloader.php new file mode 100644 index 0000000..ee7706c --- /dev/null +++ b/src/Autoloader.php @@ -0,0 +1,33 @@ +namespace = rtrim($namespace, '\\') . '\\'; + $this->baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } + + public function register(): void + { + spl_autoload_register([$this, 'loadClass']); + } + + private function loadClass(string $class): void + { + if (strpos($class, $this->namespace) !== 0) { + return; + } + + $relativeClass = substr($class, strlen($this->namespace)); + $file = $this->baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; + + if (file_exists($file)) { + require $file; + } + } +} diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/Controllers/ItemController.php b/src/Controllers/ItemController.php new file mode 100644 index 0000000..df5d513 --- /dev/null +++ b/src/Controllers/ItemController.php @@ -0,0 +1,83 @@ + 'Name is required'], 400); + } + + try { + $list = ListModel::findByUuid($listUuid); + + if (!$list) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + $item = ItemModel::create($list['id'], $input); + Router::sendResponse($item, 201); + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to create item'], 500); + } + } + + public static function update(string $listUuid, string $itemId): void + { + $input = Router::getJsonInput(); + + if (empty($input)) { + Router::sendResponse(['error' => 'No data provided'], 400); + } + + try { + $list = ListModel::findByUuid($listUuid); + + if (!$list) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + $item = ItemModel::update($list['id'], (int) $itemId, $input); + + if (!$item) { + Router::sendResponse(['error' => 'Item not found'], 404); + } + + Router::sendResponse($item, 200); + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to update item'], 500); + } + } + + public static function delete(string $listUuid, string $itemId): void + { + try { + $list = ListModel::findByUuid($listUuid); + + if (!$list) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + $deleted = ItemModel::delete($list['id'], (int) $itemId); + + if (!$deleted) { + Router::sendResponse(['error' => 'Item not found'], 404); + } + + http_response_code(204); + exit; + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to delete item'], 500); + } + } +} diff --git a/src/Controllers/ListController.php b/src/Controllers/ListController.php new file mode 100644 index 0000000..a6cbdca --- /dev/null +++ b/src/Controllers/ListController.php @@ -0,0 +1,85 @@ + 'Name is required'], 400); + } + + $sharable = $input['sharable'] ?? true; + + try { + $list = ListModel::create($input['name'], $sharable); + Router::sendResponse($list, 201); + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to create list'], 500); + } + } + + public static function show(string $uuid): void + { + try { + $list = ListModel::findByUuid($uuid); + + if (!$list) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + $items = ItemModel::findByListId($list['id']); + $list['items'] = $items; + + Router::sendResponse($list, 200); + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to retrieve list'], 500); + } + } + + public static function update(string $uuid): void + { + $input = Router::getJsonInput(); + + if (empty($input)) { + Router::sendResponse(['error' => 'No data provided'], 400); + } + + try { + $list = ListModel::update($uuid, $input); + + if (!$list) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + Router::sendResponse($list, 200); + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to update list'], 500); + } + } + + public static function delete(string $uuid): void + { + try { + $deleted = ListModel::delete($uuid); + + if (!$deleted) { + Router::sendResponse(['error' => 'List not found'], 404); + } + + http_response_code(204); + exit; + } catch (Exception $e) { + Router::sendResponse(['error' => 'Failed to delete list'], 500); + } + } +} diff --git a/src/Database.php b/src/Database.php new file mode 100644 index 0000000..f732bf4 --- /dev/null +++ b/src/Database.php @@ -0,0 +1,168 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + self::$connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + + if (!$dbExists) { + self::createSchema(); + } + } + + return self::$connection; + } + + private static function createSchema(): void + { + $sql = <<exec($sql); + } + + public static function select(string $table, array $where = []): array + { + $db = self::getConnection(); + $sql = "SELECT * FROM {$table}"; + + if (!empty($where)) { + $conditions = []; + foreach (array_keys($where) as $key) { + $conditions[] = "{$key} = :{$key}"; + } + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } + + $stmt = $db->prepare($sql); + $stmt->execute($where); + + return $stmt->fetchAll(); + } + + public static function selectOne(string $table, array $where): ?array + { + $results = self::select($table, $where); + return $results[0] ?? null; + } + + public static function insert(string $table, array $data): int + { + $db = self::getConnection(); + + $columns = implode(', ', array_keys($data)); + $placeholders = ':' . implode(', :', array_keys($data)); + + $sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})"; + + $stmt = $db->prepare($sql); + $stmt->execute($data); + + return (int) $db->lastInsertId(); + } + + public static function update(string $table, array $data, array $where): int + { + $db = self::getConnection(); + + $setParts = []; + foreach (array_keys($data) as $key) { + $setParts[] = "{$key} = :set_{$key}"; + } + + $whereParts = []; + foreach (array_keys($where) as $key) { + $whereParts[] = "{$key} = :where_{$key}"; + } + + $sql = "UPDATE {$table} SET " . implode(', ', $setParts) . ' WHERE ' . implode(' AND ', $whereParts); + + $params = []; + foreach ($data as $key => $value) { + $params["set_{$key}"] = $value; + } + foreach ($where as $key => $value) { + $params["where_{$key}"] = $value; + } + + $stmt = $db->prepare($sql); + $stmt->execute($params); + + return $stmt->rowCount(); + } + + public static function delete(string $table, array $where): int + { + $db = self::getConnection(); + + $conditions = []; + foreach (array_keys($where) as $key) { + $conditions[] = "{$key} = :{$key}"; + } + + $sql = "DELETE FROM {$table} WHERE " . implode(' AND ', $conditions); + + $stmt = $db->prepare($sql); + $stmt->execute($where); + + return $stmt->rowCount(); + } + + public static function query(string $sql, array $params = []): array + { + $db = self::getConnection(); + $stmt = $db->prepare($sql); + $stmt->execute($params); + + return $stmt->fetchAll(); + } + + public static function execute(string $sql, array $params = []): int + { + $db = self::getConnection(); + $stmt = $db->prepare($sql); + $stmt->execute($params); + + return $stmt->rowCount(); + } +} diff --git a/src/Env.php b/src/Env.php new file mode 100644 index 0000000..9373905 --- /dev/null +++ b/src/Env.php @@ -0,0 +1,107 @@ + $listId, + 'name' => $data['name'] ?? '', + 'category' => $data['category'] ?? null, + 'quantity' => $data['quantity'] ?? 1.0, + ]; + + $id = Database::insert('items', $insertData); + + return self::findById($id); + } + + public static function findById(int $id): ?array + { + $item = Database::selectOne('items', ['id' => $id]); + + if ($item) { + $item['quantity'] = (float) $item['quantity']; + } + + return $item; + } + + public static function findByListId(int $listId): array + { + $items = Database::query( + 'SELECT * FROM items WHERE list_id = :list_id AND deleted_at IS NULL ORDER BY created_at DESC', + ['list_id' => $listId] + ); + + foreach ($items as &$item) { + $item['quantity'] = (float) $item['quantity']; + } + + return $items; + } + + public static function update(int $listId, int $itemId, array $data): ?array + { + $item = Database::selectOne('items', ['id' => $itemId, 'list_id' => $listId]); + + if (!$item) { + return null; + } + + $updateData = []; + + if (isset($data['name'])) { + $updateData['name'] = $data['name']; + } + + if (isset($data['category'])) { + $updateData['category'] = $data['category']; + } + + if (isset($data['quantity'])) { + $updateData['quantity'] = $data['quantity']; + } + + if (empty($updateData)) { + return self::findById($itemId); + } + + Database::update('items', $updateData, ['id' => $itemId, 'list_id' => $listId]); + + return self::findById($itemId); + } + + public static function delete(int $listId, int $itemId): bool + { + $updated = Database::execute( + 'UPDATE items SET deleted_at = CURRENT_TIMESTAMP WHERE id = :id AND list_id = :list_id AND deleted_at IS NULL', + ['id' => $itemId, 'list_id' => $listId] + ); + + return $updated > 0; + } +} diff --git a/src/Models/ListModel.php b/src/Models/ListModel.php new file mode 100644 index 0000000..f688e83 --- /dev/null +++ b/src/Models/ListModel.php @@ -0,0 +1,77 @@ + $uuid, + 'name' => $name, + 'sharable' => $sharable ? 1 : 0, + ]); + + return self::findById($id); + } + + public static function findById(int $id): ?array + { + $list = Database::selectOne('lists', ['id' => $id]); + + if ($list) { + $list['sharable'] = (bool) $list['sharable']; + } + + return $list; + } + + public static function findByUuid(string $uuid): ?array + { + $list = Database::selectOne('lists', ['uuid' => $uuid]); + + if ($list) { + $list['sharable'] = (bool) $list['sharable']; + } + + return $list; + } + + public static function update(string $uuid, array $data): ?array + { + $list = self::findByUuid($uuid); + + if (!$list) { + return null; + } + + $updateData = []; + + if (isset($data['name'])) { + $updateData['name'] = $data['name']; + } + + if (isset($data['sharable'])) { + $updateData['sharable'] = $data['sharable'] ? 1 : 0; + } + + if (empty($updateData)) { + return $list; + } + + Database::update('lists', $updateData, ['uuid' => $uuid]); + + return self::findByUuid($uuid); + } + + public static function delete(string $uuid): bool + { + $deleted = Database::delete('lists', ['uuid' => $uuid]); + return $deleted > 0; + } +} diff --git a/src/Router.php b/src/Router.php new file mode 100644 index 0000000..b5fecfc --- /dev/null +++ b/src/Router.php @@ -0,0 +1,68 @@ +routes[] = [ + 'method' => strtoupper($method), + 'path' => $path, + 'handler' => $handler, + ]; + } + + public function dispatch(string $method, string $uri): void + { + $method = strtoupper($method); + $uri = parse_url($uri, PHP_URL_PATH); + $uri = rtrim($uri, '/') ?: '/'; + + foreach ($this->routes as $route) { + if ($route['method'] !== $method) { + continue; + } + + $pattern = $this->convertPathToRegex($route['path']); + + if (preg_match($pattern, $uri, $matches)) { + array_shift($matches); + call_user_func_array($route['handler'], $matches); + return; + } + } + + $this->sendResponse(['error' => 'Not Found'], 404); + } + + private function convertPathToRegex(string $path): string + { + $path = rtrim($path, '/') ?: '/'; + $pattern = preg_replace('/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/', '([^/]+)', $path); + return '#^' . $pattern . '$#'; + } + + public static function getJsonInput(): array + { + $input = file_get_contents('php://input'); + return json_decode($input, true) ?? []; + } + + public static function sendResponse(array $data, int $statusCode = 200): void + { + http_response_code($statusCode); + header('Content-Type: application/json'); + + if ($statusCode >= 400) { + echo json_encode(['success' => false, 'error' => $data['error'] ?? 'An error occurred']); + } else { + echo json_encode(['success' => true, 'data' => $data]); + } + + exit; + } +} diff --git a/symfony.lock b/symfony.lock deleted file mode 100644 index a5df8ea..0000000 --- a/symfony.lock +++ /dev/null @@ -1,60 +0,0 @@ -{ - "symfony/console": { - "version": "8.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "5.3", - "ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461" - }, - "files": [ - "bin/console" - ] - }, - "symfony/flex": { - "version": "2.10", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "2.4", - "ref": "52e9754527a15e2b79d9a610f98185a1fe46622a" - }, - "files": [ - ".env", - ".env.dev" - ] - }, - "symfony/framework-bundle": { - "version": "8.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "7.4", - "ref": "09f6e081c763a206802674ce0cb34a022f0ffc6d" - }, - "files": [ - "config/packages/cache.yaml", - "config/packages/framework.yaml", - "config/preload.php", - "config/routes/framework.yaml", - "config/services.yaml", - "public/index.php", - "src/Controller/.gitignore", - "src/Kernel.php", - ".editorconfig" - ] - }, - "symfony/routing": { - "version": "8.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "7.4", - "ref": "bc94c4fd86f393f3ab3947c18b830ea343e51ded" - }, - "files": [ - "config/packages/routing.yaml", - "config/routes.yaml" - ] - } -}