diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9ee462a60..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2 - -jobs: - build: - machine: true - steps: - - checkout - - - restore_cache: - keys: - - composer_{{ checksum "composer.lock" }} - - - run: cp .env.dist .env - - run: make build - # We need to wait until the mysql is ready, and since docker-compose remove the health check condition - # on depends_on, we have to do this πŸ’© https://docs.docker.com/compose/compose-file/#depends_on - - run: > - while ! docker exec codelytv-cqrs_ddd_php_example-mysql mysqladmin --user=root --password=root --host "127.0.0.1" ping --silent &> /dev/null ; do - echo "Waiting for database connection..." - sleep 2 - done - - run: make test - - store_test_results: - path: build/test_results - - store_artifacts: - path: build/test_results - - - save_cache: - key: composer_{{ checksum "composer.lock" }} - paths: - - ~/.composer diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 01490f6f8..000000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf - -[Makefile] -indent_style = tab -indent_size = 4 diff --git a/.env b/.env new file mode 100644 index 000000000..8099dcd37 --- /dev/null +++ b/.env @@ -0,0 +1,41 @@ +### Symfony - framework-bundle +APP_ENV=test +APP_SECRET=29ac4a5187930cd4b689aa0f3ee7cbc0 +#TRUSTED_PROXIES=127.0.0.1,127.0.0.2 +#TRUSTED_HOSTS='^localhost|example\.com$' + +# MOOC # +#--------------------------------# +# MySql +MOOC_DATABASE_DRIVER=pdo_mysql +MOOC_DATABASE_HOST=codely-php_ddd_skeleton-mooc-mysql +MOOC_DATABASE_PORT=3306 +MOOC_DATABASE_NAME=mooc +MOOC_DATABASE_USER=root +MOOC_DATABASE_PASSWORD= + +# BACKOFFICE # +#--------------------------------# +# MySql +BACKOFFICE_DATABASE_DRIVER=pdo_mysql +BACKOFFICE_DATABASE_HOST=codely-php_ddd_skeleton-mooc-mysql +BACKOFFICE_DATABASE_PORT=3306 +BACKOFFICE_DATABASE_NAME=mooc +BACKOFFICE_DATABASE_USER=root +BACKOFFICE_DATABASE_PASSWORD= + +# Elasticsearch +BACKOFFICE_ELASTICSEARCH_HOST=codely-php_ddd_skeleton-backoffice-elastic +BACKOFFICE_ELASTICSEARCH_INDEX_PREFIX=backoffice + +# COMMON # +#--------------------------------# +# RabbitMQ +RABBITMQ_HOST=codely-php_ddd_skeleton-rabbitmq +RABBITMQ_PORT=5672 +RABBITMQ_LOGIN=codely +RABBITMQ_PASSWORD=c0d3ly +RABBITMQ_EXCHANGE=domain_events +RABBITMQ_MAX_RETRIES=5 +# RabbitMQ - Application Specific +RABBITMQ_MOOC_VHOST=/ diff --git a/.env.dist b/.env.dist deleted file mode 100644 index e7f98b90f..000000000 --- a/.env.dist +++ /dev/null @@ -1,11 +0,0 @@ -SYMFONY_ENV=dev - -MYSQL_HOST=codelytv-cqrs_ddd_php_example-mysql -MYSQL_PORT=3306 -MYSQL_ROOT_PASSWORD=c0dely -MYSQL_DATABASE=codelytv-cqrs_ddd_php_example - -PHP_IDE_CONFIG=serverName=CodelyTvCqrsDddPhpExample - -RABBITMQ_DEFAULT_USER=codelytv -RABBITMQ_DEFAULT_PASS=c0d3ly diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..740910fde --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://bit.ly/CodelyTvPro diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 747f8b603..000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - Enhancement - - Hacktoberfest - - Bug - - ⚑ To review - - 😬 Reviewed -# Label to use when marking an issue as stale -staleLabel: Stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - Issue automatically set as `Stale` after 30 days of inactivity. - It will be closed in 7 days if we don't move it forward. - We would like to maintain the repo as clean as possible. - Thanks for your comprehension πŸ‘Ό -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: > - Issue automatically closed due to being inactive during 7 days after flagging it as `Stale`. - We would like to maintain the repo as clean as possible. Thanks for your comprehension πŸ‘Ό diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..39fe081e4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - run: docker compose pull + + - name: 🎻 Install dependencies + run: make composer-install + + - name: 🐳 Start all the environment + run: make start + + - name: πŸ”¦ Lint + run: make lint + + - name: 🏁 Static analysis + run: make static-analysis + + - name: πŸ—οΈ Architecture + run: make test-architecture + + - name: πŸ—‘οΈ Mess detector + run: make mess-detector + + - name: 🦭 Wait for the database to get up + run: | + while ! make ping-mysql &>/dev/null; do + echo "Waiting for database connection..." + sleep 2 + done + + - name: πŸ§ͺ Wait for the Elasticsearch to get up + run: | + while ! make ping-elasticsearch &>/dev/null; do + echo "Waiting for elasticsearch connection..." + sleep 2 + done + + - name: 🐰 Wait for RabbitMQ to get up + run: | + while ! make ping-rabbitmq &>/dev/null; do + echo "Waiting for RabbitMQ connection..." + sleep 2 + done + + - name: βœ… Run the tests + run: make test diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..e99bf3b81 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,18 @@ +name: labeler + +on: [pull_request] + +jobs: + labeler: + runs-on: ubuntu-latest + name: Label the PR size + steps: + - uses: codelytv/pr-size-labeler@v1.8.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + xs_max_size: '10' + s_max_size: '300' + m_max_size: '600' + l_max_size: '1400' + fail_if_xl: 'true' + files_to_ignore: 'composer.lock' diff --git a/.gitignore b/.gitignore index 81f30f819..a4b4f3437 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,15 @@ -applications/*/var/* -!applications/*/var/*/.gitkeep +/.env.local +/.env.*.local -src/*/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/*_parameters.yml -src/*/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/*_parameters.yml +/apps/*/*/var/ +!/apps/*/*/var/.gitkeep -/applications/mooc_backend/config/supervisor/* -!/applications/mooc_backend/config/supervisor/.gitkeep +/apps/*/*/build/ +!/apps/*/*/build/supervisor/.gitkeep -/phpunit.xml +/vendor/ +.phpunit.result.cache -vendor/ +/build -.env - -build/ +.php-cs-fixer.cache diff --git a/Dockerfile b/Dockerfile index 7586942af..dd0f725bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,25 @@ -FROM php:7.3.6-fpm-alpine +FROM php:8.3-fpm-alpine WORKDIR /app RUN apk --update upgrade \ - && apk add --no-cache autoconf automake make gcc g++ icu-dev rabbitmq-c rabbitmq-c-dev \ - && pecl install amqp-1.9.4 \ - && pecl install apcu-5.1.17 \ - && pecl install xdebug-2.7.0RC2 \ - && docker-php-ext-install -j$(nproc) \ + && apk add --no-cache autoconf automake make gcc g++ git bash icu-dev libzip-dev rabbitmq-c rabbitmq-c-dev linux-headers + +RUN pecl install apcu-5.1.23 && pecl install amqp-2.1.1 && pecl install xdebug-3.3.0 + +RUN docker-php-ext-install -j$(nproc) \ bcmath \ opcache \ intl \ - pdo_mysql \ - && docker-php-ext-enable \ - amqp \ - apcu \ - opcache + zip \ + pdo_mysql + +RUN docker-php-ext-enable amqp apcu opcache + +RUN curl -sS https://get.symfony.com/cli/installer | bash -s - --install-dir /usr/local/bin COPY etc/infrastructure/php/ /usr/local/etc/php/ + +# allow non-root users have home +RUN mkdir -p /opt/home +RUN chmod 777 /opt/home +ENV HOME /opt/home diff --git a/LICENCE b/LICENCE deleted file mode 100644 index 618e6b9b6..000000000 --- a/LICENCE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 CodelyTV soporte@codely.tv - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Makefile b/Makefile index 0fa4f6bb1..efec06c20 100644 --- a/Makefile +++ b/Makefile @@ -1,60 +1,54 @@ -.PHONY: all build deps composer-install composer-update composer reload test run-tests start stop destroy doco rebuild - current-dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -# πŸ‘Œ Main targets - -build: deps start - -deps: composer-install - -# 🐘 Composer - -composer-install: CMD=install -composer-update: CMD=update - -# Usage example (add a new dependency): `make composer CMD="require --dev symfony/var-dumper ^4.2"` -composer composer-install composer-update: - @docker run --rm --interactive --tty --volume $(current-dir):/app --user $(id -u):$(id -g) \ - gsingh1/prestissimo $(CMD) \ +composer-install: + @docker run --rm $(INTERACTIVE) --volume $(current-dir):/app --user $(id -u):$(id -g) \ + composer:2.6.4 install \ --ignore-platform-reqs \ - --no-ansi \ - --no-interaction - -# πŸ•΅οΈ Clear cache -# OpCache: Restarts the unique process running in the PHP FPM container -# Nginx: Reloads the server + --no-ansi -reload: - @docker-compose exec php-fpm kill -USR2 1 - @docker-compose exec nginx nginx -s reload +test: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpunit --testsuite mooc + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpunit --testsuite shared + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/behat -p mooc_backend --format=progress -v + docker exec codely-php_ddd_skeleton-backoffice_backend-php ./vendor/bin/phpunit --testsuite backoffice -# βœ… Tests +static-analysis: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/psalm --output-format=github --shepherd -test: - @docker exec -it codelytv-cqrs_ddd_php_example-php make run-tests +lint: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/ecs check -run-tests: - mkdir -p build/test_results/phpunit - ./vendor/bin/phpstan analyse -l 7 -c etc/phpstan/phpstan.neon applications/mooc_backend/src - ./vendor/bin/phpunit --exclude-group='disabled' --log-junit build/test_results/phpunit/junit.xml tests - ./vendor/bin/behat -p mooc_backend --format=progress -v +test-architecture: + docker exec codely-php_ddd_skeleton-mooc_backend-php php -d memory_limit=4G ./vendor/bin/phpstan analyse --error-format=github -# 🐳 Docker Compose +mess-detector: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpmd apps,src,tests github phpmd.xml start: - @docker-compose up -d + @if [ ! -f .env.local ]; then echo '' > .env.local; fi + UID=${shell id -u} GID=${shell id -g} docker compose up --build -d + make clean-cache -stop: CMD=stop +stop: + UID=${shell id -u} GID=${shell id -g} docker compose stop -destroy: CMD=down - -# Usage: `make doco CMD="ps --services"` -# Usage: `make doco CMD="build --parallel --pull --force-rm --no-cache"` -doco stop destroy: - @docker-compose $(CMD) +destroy: + UID=${shell id -u} GID=${shell id -g} docker compose down rebuild: - docker-compose build --pull --force-rm --no-cache - make deps + docker compose build --pull --force-rm --no-cache + make install make start + +ping-mysql: + @docker exec codely-php_ddd_skeleton-mooc-mysql mysqladmin --user=root --password= --host "127.0.0.1" ping --silent + +ping-elasticsearch: + @curl -I -XHEAD localhost:9200 + +ping-rabbitmq: + @docker exec codely-php_ddd_skeleton-rabbitmq rabbitmqctl ping --silent + +clean-cache: + @rm -rf apps/*/*/var + @docker exec codely-php_ddd_skeleton-mooc_backend-php ./apps/mooc/backend/bin/console cache:warmup diff --git a/README.md b/README.md index 41ee70a98..85d72efbe 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,79 @@

- - + + + + + Codely logo +

- 🐘🎯 Hexagonal Architecture, DDD & CQRS in PHP Symfony + 🐘🎯 Hexagonal Architecture, DDD & CQRS in PHP

- codely.tv + Codely Open Source projects CodelyTV Courses - Symfony 4.2 - CircleCI Status + Symfony 7 + Type Coverage + CI pipeline status

- Example of a PHP application following Domain-Driven Design (DDD) and - Command Query Responsibility Segregation (CQRS) principles keeping the code as simple as possible. + Example of a PHP application using Domain-Driven Design (DDD) and Command Query Responsibility Segregation + (CQRS) principles keeping the code as simple as possible.

Take a look, play and have fun with this. - Stars are welcomed 😊 -
-
- Explore the docs » + Stars are welcome 😊

View Demo Β· - Report Bug + Report a bug Β· - Request Feature + Request a feature

- - -## Table of Contents - -* [πŸš€ Environment setup](#-environment-setup) - * [🐳 Needed tools](#-needed-tools) - * [πŸ› οΈ Environment configuration](#-environment-configuration) - * [🌍 Application execution](#-application-execution) - * [βœ… Tests execution](#-tests-execution) -* [πŸ€” Project explanation](#-project-explanation) - * [Bounded Contexts](#-bounded-contexts) - * [Hexagonal Architecture](#-hexagonal-architecture) - * [Aggregates](#aggregates) - * [Command Bus](#command-bus) - * [Query Bus](#query-bus) - * [Event Bus](#event-bus) -* [🀝 Contributing](#-contributing) -* [🀩 Extra](#-extra) - -## πŸš€ Environment setup +## πŸš€ Environment Setup ### 🐳 Needed tools 1. [Install Docker](https://www.docker.com/get-started) -2. Clone this project: `git clone https://github.com/CodelyTV/cqrs-ddd-php-example cqrs-ddd-php-example` -3. Move to the project folder: `cd cqrs-ddd-php-example` +2. Clone this project: `git clone https://github.com/CodelyTV/php-ddd-example php-ddd-example` +3. Move to the project folder: `cd php-ddd-example` ### πŸ› οΈ Environment configuration -1. Copy the default environment variables: `cp .env.dist .env` -2. Modify the environment variables if needed: `vim .env` -3. Add `api.codelytv.localhost` domain to your local hosts: `echo "127.0.0.1 api.codelytv.localhost"| sudo tee -a /etc/hosts > /dev/null` +1. Create a local environment file (`cp .env .env.local`) if you want to modify any parameter -### 🌍 Application execution +### πŸ”₯ Application execution -1. Install PHP dependencies and bring up the project Docker containers with Docker Compose: `make build` -2. Go to [the API health check page](http://api.codelytv.localhost:8030/status) +1. Install all the dependencies and bring up the project with Docker executing: `make build` +2. Then you'll have 3 apps available (2 APIs and 1 Frontend): + 1. [Mooc Backend](apps/mooc/backend): http://localhost:8030/health-check + 2. [Backoffice Backend](apps/backoffice/backend): http://localhost:8040/health-check + 3. [Backoffice Frontend](apps/backoffice/frontend): http://localhost:8041/health-check ### βœ… Tests execution -1. Install PHP dependencies if you haven't done so: `make deps` -2. Execute Behat and PHP Unit tests: `make test` +1. Install the dependencies if you haven't done it previously: `make deps` +2. Execute PHPUnit and Behat tests: `make test` -## πŸ€” Project explanation +## πŸ‘©β€πŸ’» Project explanation -This project tries to be a MOOC (Massive Open Online Course) platform. -For now it only has an [API](applications/mooc_backend/src/Controller) -and some [Consumers](applications/mooc_backend/src/Command). +This project tries to be a MOOC (Massive Open Online Course) platform. It's decoupled from any framework, but it has +some Symfony and Laravel implementations. ### ⛱️ Bounded Contexts -* [Mooc](src/Mooc): Place to look in if you wanna see some code πŸ™‚. Massive Open Online Courses public platform with users, videos, notifications, and so on -* [Backoffice](src/Backoffice): Work in progress. Here you'll find the use cases needed by the Customer Support department in order to manage users, courses, videos, and so on. +- [Mooc](src/Mooc): Place to look in if you wanna see some code πŸ™‚. Massive Open Online Courses public platform with users, videos, notifications, and so on. +- [Backoffice](src/Backoffice): Here you'll find the use cases needed by the Customer Support department in order to manage users, courses, videos, and so on. ### 🎯 Hexagonal Architecture -This repository follow the Hexagonal Architecture pattern. Also is structured using `modules`. +This repository follows the Hexagonal Architecture pattern. Also, it's structured using `modules`. With this, we can see that the current structure of a Bounded Context is: ```scala @@ -121,43 +104,63 @@ src | `-- Infrastructure // The infrastructure of our module | |-- DependencyInjection | `-- Persistence -| `--VideoRepositoryMySql.php // An implementation of the repository +| `--MySqlVideoRepository.php // An implementation of the repository `-- Shared // Shared Kernel: Common infrastructure and domain shared between the different Bounded Contexts |-- Domain `-- Infrastructure ``` #### Repository pattern + Our repositories try to be as simple as possible usually only containing 2 methods `search` and `save`. -If we need some query with more filters we use the `Strategy` pattern also known as `Criteria` pattern. So we add a +If we need some query with more filters we use the `Specification` pattern also known as `Criteria` pattern. So we add a `searchByCriteria` method. -You can see an example [here](src/Mooc/Videos/Domain/VideoRepository.php) -and its implementation [here](src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php). +You can see an example [here](src/Mooc/Courses/Domain/CourseRepository.php) +and its implementation [here](src/Mooc/Courses/Infrastructure/Persistence/DoctrineCourseRepository.php). ### Aggregates -You can see an example of an aggregate [here](src/Mooc/Videos/Domain/Video.php). All aggregates should -extends the [AggregateRoot](src/Shared/Domain/Aggregate/AggregateRoot.php). + +You can see an example of an aggregate [here](src/Mooc/Courses/Domain/Course.php). All aggregates should +extend the [AggregateRoot](src/Shared/Domain/Aggregate/AggregateRoot.php). ### Command Bus -There are 2 implementations of the [command bus](src/Shared/Domain/Bus/Command/CommandBus.php). -1. [Sync](src/Shared/Infrastructure/Bus/Command/SymfonySyncCommandBus.php) using the Symfony Message Bus -2. [Async](src/Shared/Infrastructure/Bus/Command/CommandBusAsync.php) using a local file + +There is 1 implementations of the [command bus](src/Shared/Domain/Bus/Command/CommandBus.php). +1. [Sync](src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php) using the Symfony Message Bus. + ### Query Bus -The [Query Bus](src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBus.php) uses the Symfony Message Bus. + +The [Query Bus](src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php) uses the Symfony Message Bus. ### Event Bus -The [Event Bus](src/Shared/Infrastructure/Bus/Event/SymfonySyncEventBus.php) uses the Symfony Message Bus. + +The [Event Bus](src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php) uses the Symfony Message Bus. +The [MySql Bus](src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php) uses a MySql+Pulling as a bus. +The [RabbitMQ Bus](src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php) uses RabbitMQ C extension. + +## πŸ“± Monitoring + +Every time a domain event is published it's exported to Prometheus. You can access to the Prometheus panel [here](http://localhost:9999/). ## πŸ€” Contributing + There are some things missing (add swagger, improve documentation...), feel free to add this if you want! If you want some guidelines feel free to contact us :) ## 🀩 Extra -This code was show in the [From framework coupled code to #microservices through #DDD](http://codely.tv/screencasts/codigo-acoplado-framework-microservicios-ddd) talk and doubts where answered in [DDD y CQRS: Preguntas Frecuentes](http://codely.tv/screencasts/ddd-cqrs-preguntas-frecuentes/) video. + +This code was shown in the [From framework coupled code to #microservices through #DDD](http://codely.tv/blog/screencasts/codigo-acoplado-framework-microservicios-ddd) talk and doubts where answered in the [DDD y CQRS: Preguntas Frecuentes](https://codely.com/blog/ddd-cqrs-preguntas-frecuentes) video. + πŸŽ₯ Used in the CodelyTV Pro courses: -* [πŸ‡ͺπŸ‡Έ Arquitectura Hexagonal](https://pro.codely.tv/library/arquitectura-hexagonal/66748/about/) -* [πŸ‡ͺπŸ‡Έ CQRS: Command Query Responsibility Segregation](https://pro.codely.tv/library/cqrs-command-query-responsibility-segregation-3719e4aa/62554/about/) -* [πŸ‡ͺπŸ‡Έ ComunicaciΓ³n entre microservicios: Event-Driven Architecture](https://pro.codely.tv/library/comunicacion-entre-microservicios-event-driven-architecture/74823/about/) + +- [πŸ‡ͺπŸ‡Έ DDD in PHP](https://pro.codely.tv/library/ddd-en-php/about/) +- [πŸ‡ͺπŸ‡Έ Arquitectura Hexagonal](https://pro.codely.tv/library/arquitectura-hexagonal/66748/about/) +- [πŸ‡ͺπŸ‡Έ CQRS: Command Query Responsibility Segregation](https://pro.codely.tv/library/cqrs-command-query-responsibility-segregation-3719e4aa/62554/about/) +- [πŸ‡ͺπŸ‡Έ ComunicaciΓ³n entre microservicios: Event-Driven Architecture](https://pro.codely.tv/library/comunicacion-entre-microservicios-event-driven-architecture/74823/about/) + +## 🌐 remember to visit our courses + +- [Courses codely](https://codely.com/cursos) diff --git a/applications/bootstrap.php b/applications/bootstrap.php deleted file mode 100644 index e0cd2cfaf..000000000 --- a/applications/bootstrap.php +++ /dev/null @@ -1,7 +0,0 @@ -loadEnv(dirname(__DIR__) . '/.env'); diff --git a/applications/mooc_backend/bin/console b/applications/mooc_backend/bin/console deleted file mode 100755 index f38aa9998..000000000 --- a/applications/mooc_backend/bin/console +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env php -getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev'); -$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod'; - -if ($debug) { - Debug::enable(); -} - -$kernel = new MoocBackendKernel($env, $debug); -$application = new Application($kernel); -$application->run($input); diff --git a/applications/mooc_backend/config/bundles.php b/applications/mooc_backend/config/bundles.php deleted file mode 100644 index 17bf53423..000000000 --- a/applications/mooc_backend/config/bundles.php +++ /dev/null @@ -1,26 +0,0 @@ - ['all' => true], - CodelyTvMoocBundle::class => ['all' => true], - - FrameworkBundle::class => ['all' => true], - TwigBundle::class => ['all' => true], - - MonologBundle::class => ['all' => true], - - FOSRestBundle::class => ['all' => true], - JMSSerializerBundle::class => ['all' => true], - - FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true] -]; - diff --git a/applications/mooc_backend/config/commands/commands.yml b/applications/mooc_backend/config/commands/commands.yml deleted file mode 100644 index 2c64d313c..000000000 --- a/applications/mooc_backend/config/commands/commands.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - - CodelyTv\MoocBackend\Command\GenerateSupervisorFilesCommand: - arguments: ['@CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscribersConfiguration'] - tags: - - { name: console.command } diff --git a/applications/mooc_backend/config/config.yml b/applications/mooc_backend/config/config.yml deleted file mode 100644 index b1fe5ffed..000000000 --- a/applications/mooc_backend/config/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -imports: - - { resource: controllers/_controllers.yml } - - { resource: commands/commands.yml } - - { resource: rest/_rest.yml } - - { resource: logs.yml } - - { resource: symfony.yml } - - -framework: - secret: 'secret' diff --git a/applications/mooc_backend/config/config_dev.yml b/applications/mooc_backend/config/config_dev.yml deleted file mode 100644 index b4058bd5b..000000000 --- a/applications/mooc_backend/config/config_dev.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: "config.yml" } diff --git a/applications/mooc_backend/config/config_prod.yml b/applications/mooc_backend/config/config_prod.yml deleted file mode 100644 index b4058bd5b..000000000 --- a/applications/mooc_backend/config/config_prod.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: "config.yml" } diff --git a/applications/mooc_backend/config/config_test.yml b/applications/mooc_backend/config/config_test.yml deleted file mode 100644 index 3169e2776..000000000 --- a/applications/mooc_backend/config/config_test.yml +++ /dev/null @@ -1,25 +0,0 @@ -imports: - - { resource: config_dev.yml } - -parameters: - - test.client.parameters: - HTTP_HOST: - HTTP_USER_AGENT: - HTTP_ACCEPT: - HTTP_ACCEPT_LANGUAGE: - HTTP_ACCEPT_CHARSET: - SERVER_PROTOCOL: - REQUEST_TIME: - -framework: - - test: ~ - -services: - _defaults: - autowire: true - autoconfigure: true - - CodelyTv\Test\: - resource: '../../../tests/src/**/*Context.php' diff --git a/applications/mooc_backend/config/controllers/_controllers.yml b/applications/mooc_backend/config/controllers/_controllers.yml deleted file mode 100644 index 85e0942a8..000000000 --- a/applications/mooc_backend/config/controllers/_controllers.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - - { resource: course.yml } - - { resource: status.yml } - - { resource: student.yml } - - { resource: video.yml } diff --git a/applications/mooc_backend/config/controllers/course.yml b/applications/mooc_backend/config/controllers/course.yml deleted file mode 100644 index bb070c9a3..000000000 --- a/applications/mooc_backend/config/controllers/course.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - - codely.mooc_backend.controller.course.post: - class: CodelyTv\MoocBackend\Controller\Course\CoursePostController - parent: codely.mooc_backend.controller - public: true diff --git a/applications/mooc_backend/config/controllers/status.yml b/applications/mooc_backend/config/controllers/status.yml deleted file mode 100644 index d7ab6ebe0..000000000 --- a/applications/mooc_backend/config/controllers/status.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - - codely.mooc_backend.controller.status.get: - class: CodelyTv\MoocBackend\Controller\Status\StatusGetController - public: true diff --git a/applications/mooc_backend/config/controllers/student.yml b/applications/mooc_backend/config/controllers/student.yml deleted file mode 100644 index fc9922532..000000000 --- a/applications/mooc_backend/config/controllers/student.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - - codely.mooc_backend.controller.student.put: - class: CodelyTv\MoocBackend\Controller\Student\StudentPutController - parent: codely.mooc_backend.controller - public: true - - codely.mooc_backend.controller.student.get: - class: CodelyTv\MoocBackend\Controller\Student\StudentGetController - parent: codely.mooc_backend.controller - public: true diff --git a/applications/mooc_backend/config/controllers/video.yml b/applications/mooc_backend/config/controllers/video.yml deleted file mode 100644 index 3d617d9c2..000000000 --- a/applications/mooc_backend/config/controllers/video.yml +++ /dev/null @@ -1,16 +0,0 @@ -services: - - codely.mooc_backend.controller.video.post: - class: CodelyTv\MoocBackend\Controller\Video\VideoPostController - parent: codely.mooc_backend.controller - public: true - - codely.mooc_backend.controller.video.get: - class: CodelyTv\MoocBackend\Controller\Video\VideoGetController - parent: codely.mooc_backend.controller - public: true - - codely.mooc_backend.controller.video.duration.path: - class: CodelyTv\MoocBackend\Controller\Video\VideoDurationPatchController - parent: codely.mooc_backend.controller - public: true diff --git a/applications/mooc_backend/config/logs.yml b/applications/mooc_backend/config/logs.yml deleted file mode 100644 index f94d0e5e1..000000000 --- a/applications/mooc_backend/config/logs.yml +++ /dev/null @@ -1,34 +0,0 @@ -monolog: - - handlers: - main: - type: stream - level: warning - formatter: codely.infrastructure.monolog.formatter.logstash - channels: ['!api'] - api_request: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.api.requests.log" - level: warning - formatter: codely.infrastructure.monolog.formatter.logstash - channels: ['api'] - events: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.events.log" - level: error - formatter: codely.infrastructure.monolog.formatter.logstash - channels: ['events'] - channels: ['api', 'events'] - - -services: - - monolog.processor.codely_request: - class: CodelyTv\Shared\Infrastructure\Monolog\Processor\CodelyTvRequestProcessor - arguments: - - '@request_stack' - tags: - - { name: monolog.processor, handler: api_request } - - codely.infrastructure.monolog.formatter.logstash: - class: CodelyTv\Shared\Infrastructure\Monolog\Formatter\LogstashFormatter diff --git a/applications/mooc_backend/config/rest/_rest.yml b/applications/mooc_backend/config/rest/_rest.yml deleted file mode 100644 index 8aa563825..000000000 --- a/applications/mooc_backend/config/rest/_rest.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - { resource: event_subscribers.yml } - - { resource: fos_rest.yml } - - { resource: jms_serializer.yml } - - { resource: controllers.yml } - - { resource: twig.yml } diff --git a/applications/mooc_backend/config/rest/controllers.yml b/applications/mooc_backend/config/rest/controllers.yml deleted file mode 100644 index bd8c92aec..000000000 --- a/applications/mooc_backend/config/rest/controllers.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - - codely.mooc_backend.controller: - class: CodelyTv\Shared\Infrastructure\Api\Controller\ApiController - abstract: true - arguments: - - '@CodelyTv\Shared\Domain\Bus\Query\QueryBus' - - '@CodelyTv\Shared\Domain\Bus\Command\CommandBus' - - '@CodelyTv\Shared\Infrastructure\Api\Exception\ApiExceptionsHttpStatusCodeMapping' diff --git a/applications/mooc_backend/config/rest/event_subscribers.yml b/applications/mooc_backend/config/rest/event_subscribers.yml deleted file mode 100644 index d1a2c0faf..000000000 --- a/applications/mooc_backend/config/rest/event_subscribers.yml +++ /dev/null @@ -1,24 +0,0 @@ -services: - - codely.mooc_backend.subscriber.exception_response: - class: CodelyTv\Shared\Infrastructure\Api\EventSubscriber\ApiExceptionSubscriber - arguments: - - '@fos_rest.view_handler.default' - - '@CodelyTv\Shared\Infrastructure\Api\Exception\ApiExceptionsHttpStatusCodeMapping' - tags: - - { name: kernel.event_subscriber } - - codely.mooc_backend.subscriber.exception_logger: - class: CodelyTv\Shared\Infrastructure\Api\EventSubscriber\ApiExceptionLoggerSubscriber - arguments: - - '@monolog.logger.api' - - '@CodelyTv\Shared\Infrastructure\Api\Exception\ApiExceptionsHttpStatusCodeMapping' - tags: - - { name: kernel.event_subscriber } - - codely.mooc_backend.subscriber.response_view: - class: CodelyTv\Shared\Infrastructure\Api\EventSubscriber\ApiResponseViewSubscriber - tags: - - { name: kernel.event_subscriber } - - CodelyTv\Shared\Infrastructure\Api\Exception\ApiExceptionsHttpStatusCodeMapping: ~ diff --git a/applications/mooc_backend/config/rest/fos_rest.yml b/applications/mooc_backend/config/rest/fos_rest.yml deleted file mode 100644 index 6cf85f0b8..000000000 --- a/applications/mooc_backend/config/rest/fos_rest.yml +++ /dev/null @@ -1,14 +0,0 @@ -fos_rest: - - format_listener: - rules: - - path: ~ - host: ~ - prefer_extension: false - fallback_format: json - - view: - view_response_listener: force - - serializer: - serialize_null: true diff --git a/applications/mooc_backend/config/rest/jms_serializer.yml b/applications/mooc_backend/config/rest/jms_serializer.yml deleted file mode 100644 index 5e9d19964..000000000 --- a/applications/mooc_backend/config/rest/jms_serializer.yml +++ /dev/null @@ -1,21 +0,0 @@ -jms_serializer: - - visitors: - json: - options: JSON_PRETTY_PRINT - - -services: - - codely.mooc_backend.serializer.metadata.php_driver: - class: CodelyTv\Shared\Infrastructure\Api\Serializer\ApiSerializerDriver - - jms_serializer.metadata_driver: - alias: codely.mooc_backend.serializer.metadata.php_driver - public: true - - -parameters: - - jms_serializer.infer_types_from_doctrine_metadata: false - jms_serializer.datetime_handler.class: CodelyTv\Shared\Infrastructure\Api\Serializer\DateTimeHandler diff --git a/applications/mooc_backend/config/rest/twig.yml b/applications/mooc_backend/config/rest/twig.yml deleted file mode 100644 index 42d272f99..000000000 --- a/applications/mooc_backend/config/rest/twig.yml +++ /dev/null @@ -1,2 +0,0 @@ -twig: - strict_variables: '%kernel.debug%' diff --git a/applications/mooc_backend/config/routing/_routing.yml b/applications/mooc_backend/config/routing/_routing.yml deleted file mode 100644 index 93944875c..000000000 --- a/applications/mooc_backend/config/routing/_routing.yml +++ /dev/null @@ -1,8 +0,0 @@ -status: - resource: status.yml - -student: - resource: student.yml - -video: - resource: video.yml diff --git a/applications/mooc_backend/config/routing/status.yml b/applications/mooc_backend/config/routing/status.yml deleted file mode 100644 index 2a63a7d88..000000000 --- a/applications/mooc_backend/config/routing/status.yml +++ /dev/null @@ -1,4 +0,0 @@ -status_get: - path: /status - defaults: { _controller: codely.mooc_backend.controller.status.get } - methods: [GET] diff --git a/applications/mooc_backend/config/routing/student.yml b/applications/mooc_backend/config/routing/student.yml deleted file mode 100644 index cb40bf800..000000000 --- a/applications/mooc_backend/config/routing/student.yml +++ /dev/null @@ -1,9 +0,0 @@ -student_put: - path: /students/{id} - defaults: { _controller: codely.mooc_backend.controller.student.put } - methods: [PUT] - -student_get: - path: /students/{id} - defaults: { _controller: codely.mooc_backend.controller.student.get } - methods: [GET] diff --git a/applications/mooc_backend/config/routing/video.yml b/applications/mooc_backend/config/routing/video.yml deleted file mode 100644 index cdef44024..000000000 --- a/applications/mooc_backend/config/routing/video.yml +++ /dev/null @@ -1,9 +0,0 @@ -video_post: - path: /video - defaults: { _controller: codely.mooc_backend.controller.video.post } - methods: [POST] - -video_get: - path: /video/{id} - defaults: { _controller: codely.mooc_backend.controller.video.get } - methods: [GET] diff --git a/applications/mooc_backend/config/symfony.yml b/applications/mooc_backend/config/symfony.yml deleted file mode 100644 index dcb4795f7..000000000 --- a/applications/mooc_backend/config/symfony.yml +++ /dev/null @@ -1,12 +0,0 @@ -framework: - - secret: 'secret' - router: - resource: '%kernel.root_dir%/config/routing/_routing.yml' - strict_requirements: '%kernel.debug%' - templating: - engines: ['twig'] - default_locale: 'en' - session: ~ - fragments: ~ - http_method_override: true diff --git a/applications/mooc_backend/src/Command/ConsumeDomainEventsCommand.php b/applications/mooc_backend/src/Command/ConsumeDomainEventsCommand.php deleted file mode 100644 index 05e479f1e..000000000 --- a/applications/mooc_backend/src/Command/ConsumeDomainEventsCommand.php +++ /dev/null @@ -1,61 +0,0 @@ -consumer = $consumer; - $this->mapping = $mapping; - $this->connections = $connections; - } - - protected function configure(): void - { - $this - ->setName('codelytv:domain-events:consume') - ->setDescription('Consume domain events') - ->addArgument('subscriber', InputArgument::REQUIRED, 'Subscriber to process') - ->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of events to process'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - /** @var string $subscriberName */ - $subscriberName = $input->getArgument('subscriber'); - /** @var int $messagesToProcess */ - $messagesToProcess = $input->getArgument('quantity'); - - repeat(pipe($this->consume($subscriberName), $this->connections->allConnectionsClearer()), $messagesToProcess); - } - - private function consume(string $subscriberName): callable - { - return function () use ($subscriberName) { - apply($this->consumer, [$this->mapping->byName($subscriberName), $subscriberName]); - }; - } -} diff --git a/applications/mooc_backend/src/Command/GenerateSupervisorFilesCommand.php b/applications/mooc_backend/src/Command/GenerateSupervisorFilesCommand.php deleted file mode 100644 index a518db485..000000000 --- a/applications/mooc_backend/src/Command/GenerateSupervisorFilesCommand.php +++ /dev/null @@ -1,75 +0,0 @@ -configuration = $configuration; - } - - protected function configure(): void - { - $this - ->setName('codelytv:domain-events:generate-supervisor-files') - ->setDescription('Generate the supervisor configuration for every subscriber') - ->addArgument('command-path', InputArgument::OPTIONAL, 'Path on this is gonna be deployed', '/var/www'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - /** @var string $path */ - $path = $input->getArgument('command-path'); - - each($this->configCreator($path), $this->configuration->all()); - } - - private function configCreator(string $path): callable - { - return function (DomainEventSubscriberConfig $config) use ($path) { - $fileContent = str_replace( - ['{subscriber}', '{path}', '{processes}', '{events_to_process}', '{enabled}'], - [$config->name(), $path, $config->processes(), $config->eventsToProcess(), $config->enabledString()], - $this->template() - ); - - file_put_contents($this->fileName($config->name()), $fileContent); - }; - } - - private function template(): string - { - return <<asyncRequestFinder = $asyncRequestFinder; - $this->exceptionHandler = $exceptionHandler; - - each($this->exceptionRegistrar(), $this->exceptions()); - } - - public function __invoke($requestId) - { - $asyncRequest = $this->asyncRequestFinder->__invoke(new Uuid($requestId)); - - return $asyncRequest->toArray(); - } - - private function exceptions(): array - { - return [ - AsyncRequestNotExists::class => Response::HTTP_NOT_FOUND, - ]; - } - - private function exceptionRegistrar(): callable - { - return function ($httpCode, $exception) { - $this->exceptionHandler->register($exception, $httpCode); - }; - } -} diff --git a/applications/mooc_backend/src/Controller/Course/CoursePostController.php b/applications/mooc_backend/src/Controller/Course/CoursePostController.php deleted file mode 100644 index 7991526fa..000000000 --- a/applications/mooc_backend/src/Controller/Course/CoursePostController.php +++ /dev/null @@ -1,33 +0,0 @@ -get('request_id')), - $request->get('id'), - $request->get('title'), - $request->get('description') - ); - - $this->dispatch($command); - - return new ApiHttpCreatedResponse(); - } -} diff --git a/applications/mooc_backend/src/Controller/Status/StatusGetController.php b/applications/mooc_backend/src/Controller/Status/StatusGetController.php deleted file mode 100644 index b455ecaba..000000000 --- a/applications/mooc_backend/src/Controller/Status/StatusGetController.php +++ /dev/null @@ -1,15 +0,0 @@ - 'OK']); - } -} diff --git a/applications/mooc_backend/src/Controller/Student/StudentGetController.php b/applications/mooc_backend/src/Controller/Student/StudentGetController.php deleted file mode 100644 index b51463eda..000000000 --- a/applications/mooc_backend/src/Controller/Student/StudentGetController.php +++ /dev/null @@ -1,25 +0,0 @@ - Response::HTTP_NOT_FOUND, - ]; - } - - public function __invoke(string $id) - { - return $this->ask(new FindStudentQuery($id)); - } -} diff --git a/applications/mooc_backend/src/Controller/Student/StudentPutController.php b/applications/mooc_backend/src/Controller/Student/StudentPutController.php deleted file mode 100644 index aa852ada1..000000000 --- a/applications/mooc_backend/src/Controller/Student/StudentPutController.php +++ /dev/null @@ -1,21 +0,0 @@ -get('request_id')); - - $command = new TrimVideoCommand( - $requestId, - $videoId, - $request->get('keep_from_second'), - $request->get('keep_to_second') - ); - - $this->dispatch($command); - - return new ApiHttpAcceptedResponse($request->getPathInfo(), $requestId); - } -} diff --git a/applications/mooc_backend/src/Controller/Video/VideoGetController.php b/applications/mooc_backend/src/Controller/Video/VideoGetController.php deleted file mode 100644 index 9d559b0ad..000000000 --- a/applications/mooc_backend/src/Controller/Video/VideoGetController.php +++ /dev/null @@ -1,25 +0,0 @@ - Response::HTTP_NOT_FOUND, - ]; - } - - public function __invoke(string $id) - { - return $this->ask(new FindVideoQuery($id)); - } -} diff --git a/applications/mooc_backend/src/Controller/Video/VideoPostController.php b/applications/mooc_backend/src/Controller/Video/VideoPostController.php deleted file mode 100644 index 04b57969c..000000000 --- a/applications/mooc_backend/src/Controller/Video/VideoPostController.php +++ /dev/null @@ -1,35 +0,0 @@ -get('request_id')), - $request->get('id'), - $request->get('type'), - $request->get('title'), - $request->get('url'), - $request->get('course_id') - ); - - $this->dispatch($command); - - return new ApiHttpCreatedResponse(); - } -} diff --git a/applications/mooc_backend/src/MoocBackendKernel.php b/applications/mooc_backend/src/MoocBackendKernel.php deleted file mode 100644 index dd9305369..000000000 --- a/applications/mooc_backend/src/MoocBackendKernel.php +++ /dev/null @@ -1,74 +0,0 @@ -getRootDir() . '/config/bundles.php'; - - return map($this->instanciateBundle(), filter($this->hasToBeRemoved(), $bundles)); - } - - public function getCacheDir() - { - return $this->getRootDir() . '/var/cache'; - } - - public function getLogDir() - { - return $this->getRootDir() . '/var/logs'; - } - - public function registerContainerConfiguration(LoaderInterface $loader) - { - $loader->load($this->getRootDir() . '/config/config_' . $this->getEnvironment() . '.yml'); - } - - public function getContainer() - { - $this->bootKernelInTestEnvironmentToDiscoverErrorsWhenDeveloping(); - - return parent::getContainer(); - } - - private function bootKernelInTestEnvironmentToDiscoverErrorsWhenDeveloping(): void - { - if ('test' === $this->getEnvironment()) { - $this->boot(); - } - } - - public function getRootDir(): string - { - return dirname(__DIR__); - } - - private function hasToBeRemoved(): callable - { - return function (array $environmentOptions) { - if ('test' === $this->getEnvironment()) { - return true; - } - - return !array_key_exists('test', $environmentOptions); - }; - } - - private function instanciateBundle(): callable - { - return function (array $unused, $class) { - return new $class(); - }; - } -} diff --git a/applications/mooc_backend/web/api.php b/applications/mooc_backend/web/api.php deleted file mode 100644 index 15afdc26f..000000000 --- a/applications/mooc_backend/web/api.php +++ /dev/null @@ -1,18 +0,0 @@ -server->get('REMOTE_ADDR')], Request::HEADER_X_FORWARDED_ALL); - -$response = $kernel->handle($request); -$response->send(); -$kernel->terminate($request, $response); diff --git a/apps/backoffice/backend/bin/console b/apps/backoffice/backend/bin/console new file mode 100755 index 000000000..29bc8b83f --- /dev/null +++ b/apps/backoffice/backend/bin/console @@ -0,0 +1,42 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__) . '/../../bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new BackofficeBackendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$application = new Application($kernel); +$application->run($input); diff --git a/apps/backoffice/backend/config/bundles.php b/apps/backoffice/backend/config/bundles.php new file mode 100644 index 000000000..15c297c9f --- /dev/null +++ b/apps/backoffice/backend/config/bundles.php @@ -0,0 +1,9 @@ + ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], + // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] +]; diff --git a/apps/backoffice/backend/config/routes/courses.yaml b/apps/backoffice/backend/config/routes/courses.yaml new file mode 100644 index 000000000..06ae46ee8 --- /dev/null +++ b/apps/backoffice/backend/config/routes/courses.yaml @@ -0,0 +1,5 @@ +courses_get: + path: /courses + controller: CodelyTv\Apps\Backoffice\Backend\Controller\Courses\CoursesGetController + defaults: { auth: false } + methods: [GET] diff --git a/apps/backoffice/backend/config/routes/health-check.yaml b/apps/backoffice/backend/config/routes/health-check.yaml new file mode 100644 index 000000000..37223ceda --- /dev/null +++ b/apps/backoffice/backend/config/routes/health-check.yaml @@ -0,0 +1,4 @@ +health-check_get: + path: /health-check + controller: CodelyTv\Apps\Backoffice\Backend\Controller\HealthCheck\HealthCheckGetController + methods: [GET] diff --git a/apps/backoffice/backend/config/routes/metrics.yaml b/apps/backoffice/backend/config/routes/metrics.yaml new file mode 100644 index 000000000..af36aa15d --- /dev/null +++ b/apps/backoffice/backend/config/routes/metrics.yaml @@ -0,0 +1,4 @@ +metrics_get: + path: /metrics + controller: CodelyTv\Apps\Backoffice\Backend\Controller\Metrics\MetricsController + methods: [GET] diff --git a/apps/backoffice/backend/config/services.yaml b/apps/backoffice/backend/config/services.yaml new file mode 100644 index 000000000..d0e25db62 --- /dev/null +++ b/apps/backoffice/backend/config/services.yaml @@ -0,0 +1,98 @@ +imports: + - { resource: ../../../../src/Backoffice/Shared/Infrastructure/Symfony/DependencyInjection/backoffice_services.yaml } + +services: + _defaults: + autoconfigure: true + autowire: true + + # Configure + _instanceof: + CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber: + tags: ['codely.domain_event_subscriber'] + + CodelyTv\Shared\Domain\Bus\Command\CommandHandler: + tags: ['codely.command_handler'] + + CodelyTv\Shared\Domain\Bus\Query\QueryHandler: + tags: ['codely.query_handler'] + + CodelyTv\Apps\Backoffice\Backend\Controller\: + resource: '../src/Controller' + tags: ['controller.service_arguments'] + + + # Wire + CodelyTv\Shared\: + resource: '../../../../src/Shared' + + CodelyTv\Backoffice\: + resource: '../../../../src/Backoffice' + + # -- TAGGING -- + CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus: + arguments: [!tagged codely.domain_event_subscriber] + lazy: true + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventMapping: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections: + arguments: [!tagged codely.database_connection] + + CodelyTv\Shared\Infrastructure\Symfony\AddJsonBodyToRequestListener: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + + CodelyTv\Shared\Infrastructure\Symfony\ApiExceptionListener: + tags: + - { name: kernel.event_listener, event: kernel.exception, method: onException } + + CodelyTv\Shared\Infrastructure\Symfony\BasicHttpAuthMiddleware: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + + + # -- APP DEFINITIONS -- + # Command/Query Handlers + CodelyTv\Shared\Infrastructure\Bus\Command\InMemorySymfonyCommandBus: + arguments: [!tagged codely.command_handler] + + CodelyTv\Shared\Infrastructure\Bus\Query\InMemorySymfonyQueryBus: + arguments: [!tagged codely.query_handler] + + # RabbitMQ + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection: + arguments: + - host: '%env(RABBITMQ_HOST)%' + port: '%env(RABBITMQ_PORT)%' + vhost: '%env(RABBITMQ_MOOC_VHOST)%' + login: '%env(RABBITMQ_LOGIN)%' + password: '%env(RABBITMQ_PASSWORD)%' + read_timeout: 2 + write_timeout: 2 + connect_timeout: 5 + + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection', '%env(RABBITMQ_EXCHANGE)%'] + + CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClient: + factory: '@CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClientFactory' + arguments: + - '%env(BACKOFFICE_ELASTICSEARCH_HOST)%' + - '%env(BACKOFFICE_ELASTICSEARCH_INDEX_PREFIX)%' + - '%kernel.project_dir%/../../../etc/databases/backoffice' + - '%env(APP_ENV)%' + public: true + + CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Monitoring\PrometheusMonitor', 'backoffice_backend', '@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus'] + + # -- IMPLEMENTATIONS SELECTOR -- + + # -- IMPLEMENTATIONS SELECTOR -- + CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus' + CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository: '@CodelyTv\Backoffice\Courses\Infrastructure\Persistence\ElasticsearchBackofficeCourseRepository' diff --git a/apps/backoffice/backend/config/services/framework.yaml b/apps/backoffice/backend/config/services/framework.yaml new file mode 100644 index 000000000..b7cea6492 --- /dev/null +++ b/apps/backoffice/backend/config/services/framework.yaml @@ -0,0 +1,17 @@ +framework: + secret: '%env(APP_SECRET)%' + #csrf_protection: true + #http_method_override: true + + # Enables session support. Note that the session will ONLY be started if you read or write from it. + # Remove or comment this section to explicitly disable session support. + session: + handler_id: null + cookie_secure: auto + cookie_samesite: lax + enabled: true + + #esi: true + #fragments: true + php_errors: + log: true diff --git a/apps/backoffice/backend/config/services_test.yaml b/apps/backoffice/backend/config/services_test.yaml new file mode 100644 index 000000000..3a81827df --- /dev/null +++ b/apps/backoffice/backend/config/services_test.yaml @@ -0,0 +1,13 @@ +framework: + test: true + +services: + _defaults: + autoconfigure: true + autowire: true + + CodelyTv\Tests\: + resource: '../../../../tests' + + # -- IMPLEMENTATIONS SELECTOR -- + CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus' diff --git a/apps/backoffice/backend/public/index.php b/apps/backoffice/backend/public/index.php new file mode 100644 index 000000000..45fd7eab5 --- /dev/null +++ b/apps/backoffice/backend/public/index.php @@ -0,0 +1,32 @@ +handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/apps/backoffice/backend/src/BackofficeBackendKernel.php b/apps/backoffice/backend/src/BackofficeBackendKernel.php new file mode 100644 index 000000000..321bf5524 --- /dev/null +++ b/apps/backoffice/backend/src/BackofficeBackendKernel.php @@ -0,0 +1,46 @@ +getProjectDir() . '/config/bundles.php'; + foreach ($contents as $class => $envs) { + if ($envs[$this->environment] ?? $envs['all'] ?? false) { + yield new $class(); + } + } + } + + public function getProjectDir(): string + { + return dirname(__DIR__); + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + { + $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); + $container->setParameter('.container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir() . '/config'; + + $loader->load($confDir . '/services' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services_' . $this->environment . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services/*' . self::CONFIG_EXTS, 'glob'); + } +} diff --git a/apps/backoffice/backend/src/Controller/Courses/CoursesGetController.php b/apps/backoffice/backend/src/Controller/Courses/CoursesGetController.php new file mode 100644 index 000000000..4fcf89a26 --- /dev/null +++ b/apps/backoffice/backend/src/Controller/Courses/CoursesGetController.php @@ -0,0 +1,51 @@ +query->get('order_by'); + $order = $request->query->get('order'); + $limit = $request->query->get('limit'); + $offset = $request->query->get('offset'); + + /** @var BackofficeCoursesResponse $response */ + $response = $this->queryBus->ask( + new SearchBackofficeCoursesByCriteriaQuery( + (array) $request->query->get('filters'), + $orderBy, + $order, + $limit === null ? null : (int) $limit, + $offset === null ? null : (int) $offset + ) + ); + + return new JsonResponse( + map( + fn (BackofficeCourseResponse $course): array => [ + 'id' => $course->id(), + 'name' => $course->name(), + 'duration' => $course->duration(), + ], + $response->courses() + ), + 200, + ['Access-Control-Allow-Origin' => '*'] + ); + } +} diff --git a/apps/backoffice/backend/src/Controller/HealthCheck/HealthCheckGetController.php b/apps/backoffice/backend/src/Controller/HealthCheck/HealthCheckGetController.php new file mode 100644 index 000000000..fe2256009 --- /dev/null +++ b/apps/backoffice/backend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -0,0 +1,20 @@ + 'ok', + ] + ); + } +} diff --git a/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php b/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php new file mode 100644 index 000000000..e0ae59059 --- /dev/null +++ b/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php @@ -0,0 +1,23 @@ +render($this->monitor->registry()->getMetricFamilySamples()); + + return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); + } +} diff --git a/applications/mooc_backend/config/supervisor/.gitkeep b/apps/backoffice/backend/var/.gitkeep similarity index 100% rename from applications/mooc_backend/config/supervisor/.gitkeep rename to apps/backoffice/backend/var/.gitkeep diff --git a/apps/backoffice/frontend/bin/console b/apps/backoffice/frontend/bin/console new file mode 100755 index 000000000..d680ab53c --- /dev/null +++ b/apps/backoffice/frontend/bin/console @@ -0,0 +1,42 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__) . '/../../bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new BackofficeFrontendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$application = new Application($kernel); +$application->run($input); diff --git a/apps/backoffice/frontend/config/bundles.php b/apps/backoffice/frontend/config/bundles.php new file mode 100644 index 000000000..ef8009558 --- /dev/null +++ b/apps/backoffice/frontend/config/bundles.php @@ -0,0 +1,10 @@ + ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] +]; diff --git a/apps/backoffice/frontend/config/routes/api_courses.yaml b/apps/backoffice/frontend/config/routes/api_courses.yaml new file mode 100644 index 000000000..3be1e106f --- /dev/null +++ b/apps/backoffice/frontend/config/routes/api_courses.yaml @@ -0,0 +1,4 @@ +api_courses_get: + path: /api/courses + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\Courses\ApiCoursesGetController + methods: [GET] diff --git a/apps/backoffice/frontend/config/routes/courses.yaml b/apps/backoffice/frontend/config/routes/courses.yaml new file mode 100644 index 000000000..bb3b30cde --- /dev/null +++ b/apps/backoffice/frontend/config/routes/courses.yaml @@ -0,0 +1,9 @@ +courses_get: + path: /courses + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\Courses\CoursesGetWebController + methods: [GET] + +courses_post: + path: /courses + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\Courses\CoursesPostWebController + methods: [POST] diff --git a/apps/backoffice/frontend/config/routes/health-check.yaml b/apps/backoffice/frontend/config/routes/health-check.yaml new file mode 100644 index 000000000..c5035a4bc --- /dev/null +++ b/apps/backoffice/frontend/config/routes/health-check.yaml @@ -0,0 +1,4 @@ +health-check_get: + path: /health-check + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\HealthCheck\HealthCheckGetController + methods: [GET] diff --git a/apps/backoffice/frontend/config/routes/home.yaml b/apps/backoffice/frontend/config/routes/home.yaml new file mode 100644 index 000000000..687b22467 --- /dev/null +++ b/apps/backoffice/frontend/config/routes/home.yaml @@ -0,0 +1,4 @@ +home_get: + path: / + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\Home\HomeGetWebController + methods: [GET] diff --git a/apps/backoffice/frontend/config/routes/metrics.yaml b/apps/backoffice/frontend/config/routes/metrics.yaml new file mode 100644 index 000000000..65ad0004d --- /dev/null +++ b/apps/backoffice/frontend/config/routes/metrics.yaml @@ -0,0 +1,4 @@ +metrics_get: + path: /metrics + controller: CodelyTv\Apps\Backoffice\Frontend\Controller\Metrics\MetricsController + methods: [GET] diff --git a/apps/backoffice/frontend/config/services.yaml b/apps/backoffice/frontend/config/services.yaml new file mode 100644 index 000000000..23de22805 --- /dev/null +++ b/apps/backoffice/frontend/config/services.yaml @@ -0,0 +1,106 @@ +imports: + - { resource: ../../../../src/Backoffice/Shared/Infrastructure/Symfony/DependencyInjection/backoffice_services.yaml } + - { resource: ../../../../src/Mooc/Shared/Infrastructure/Symfony/DependencyInjection/mooc_services.yaml } + +framework: + session: + handler_id: null + +services: + _defaults: + autoconfigure: true + autowire: true + + # Configure + _instanceof: + CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber: + tags: ['codely.domain_event_subscriber'] + + CodelyTv\Shared\Domain\Bus\Command\CommandHandler: + tags: ['codely.command_handler'] + + CodelyTv\Shared\Domain\Bus\Query\QueryHandler: + tags: ['codely.query_handler'] + + CodelyTv\Apps\Backoffice\Frontend\Controller\: + resource: '../src/Controller' + tags: ['controller.service_arguments'] + + + # Wire + CodelyTv\Shared\: + resource: '../../../../src/Shared' + + CodelyTv\Backoffice\: + resource: '../../../../src/Backoffice' + + CodelyTv\Mooc\: + resource: '../../../../src/Mooc' + + # -- TAGGING -- + CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus: + arguments: [!tagged codely.domain_event_subscriber] + lazy: true + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventMapping: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections: + arguments: [!tagged codely.database_connection] + + CodelyTv\Shared\Infrastructure\Symfony\ApiExceptionListener: + tags: + - { name: kernel.event_listener, event: kernel.exception, method: onException } + + CodelyTv\Shared\Infrastructure\Symfony\AddJsonBodyToRequestListener: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + + + # -- APP DEFINITIONS -- + # Command/Query Handlers + CodelyTv\Shared\Infrastructure\Bus\Command\InMemorySymfonyCommandBus: + arguments: [!tagged codely.command_handler] + + CodelyTv\Shared\Infrastructure\Bus\Query\InMemorySymfonyQueryBus: + arguments: [!tagged codely.query_handler] + + # RabbitMQ + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection: + arguments: + - host: '%env(RABBITMQ_HOST)%' + port: '%env(RABBITMQ_PORT)%' + vhost: '%env(RABBITMQ_MOOC_VHOST)%' + login: '%env(RABBITMQ_LOGIN)%' + password: '%env(RABBITMQ_PASSWORD)%' + read_timeout: 2 + write_timeout: 2 + connect_timeout: 5 + + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection', '%env(RABBITMQ_EXCHANGE)%'] + + CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClient: + factory: '@CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClientFactory' + arguments: + - '%env(BACKOFFICE_ELASTICSEARCH_HOST)%' + - '%env(BACKOFFICE_ELASTICSEARCH_INDEX_PREFIX)%' + - '%kernel.project_dir%/../../../etc/databases/backoffice' + - '%env(APP_ENV)%' + public: true + + CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Monitoring\PrometheusMonitor', 'backoffice_frontend', '@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus'] + + # -- IMPLEMENTATIONS SELECTOR -- + CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus' + CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository: '@CodelyTv\Backoffice\Courses\Infrastructure\Persistence\ElasticsearchBackofficeCourseRepository' + +twig: + default_path: '%kernel.project_dir%/templates' + strict_variables: true + globals: + flash: '@CodelyTv\Shared\Infrastructure\Symfony\FlashSession' diff --git a/apps/backoffice/frontend/config/services/framework.yaml b/apps/backoffice/frontend/config/services/framework.yaml new file mode 100644 index 000000000..b7cea6492 --- /dev/null +++ b/apps/backoffice/frontend/config/services/framework.yaml @@ -0,0 +1,17 @@ +framework: + secret: '%env(APP_SECRET)%' + #csrf_protection: true + #http_method_override: true + + # Enables session support. Note that the session will ONLY be started if you read or write from it. + # Remove or comment this section to explicitly disable session support. + session: + handler_id: null + cookie_secure: auto + cookie_samesite: lax + enabled: true + + #esi: true + #fragments: true + php_errors: + log: true diff --git a/apps/backoffice/frontend/config/services_test.yaml b/apps/backoffice/frontend/config/services_test.yaml new file mode 100644 index 000000000..3a81827df --- /dev/null +++ b/apps/backoffice/frontend/config/services_test.yaml @@ -0,0 +1,13 @@ +framework: + test: true + +services: + _defaults: + autoconfigure: true + autowire: true + + CodelyTv\Tests\: + resource: '../../../../tests' + + # -- IMPLEMENTATIONS SELECTOR -- + CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus' diff --git a/apps/backoffice/frontend/public/images/logo.png b/apps/backoffice/frontend/public/images/logo.png new file mode 100644 index 000000000..759395922 Binary files /dev/null and b/apps/backoffice/frontend/public/images/logo.png differ diff --git a/apps/backoffice/frontend/public/index.php b/apps/backoffice/frontend/public/index.php new file mode 100644 index 000000000..6dd7c188e --- /dev/null +++ b/apps/backoffice/frontend/public/index.php @@ -0,0 +1,32 @@ +handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/apps/backoffice/frontend/src/BackofficeFrontendKernel.php b/apps/backoffice/frontend/src/BackofficeFrontendKernel.php new file mode 100644 index 000000000..ae109b24a --- /dev/null +++ b/apps/backoffice/frontend/src/BackofficeFrontendKernel.php @@ -0,0 +1,46 @@ +getProjectDir() . '/config/bundles.php'; + foreach ($contents as $class => $envs) { + if ($envs[$this->environment] ?? $envs['all'] ?? false) { + yield new $class(); + } + } + } + + public function getProjectDir(): string + { + return dirname(__DIR__); + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + { + $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); + $container->setParameter('.container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir() . '/config'; + + $loader->load($confDir . '/services' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services_' . $this->environment . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services/*' . self::CONFIG_EXTS, 'glob'); + } +} diff --git a/apps/backoffice/frontend/src/Command/ImportCoursesToElasticsearchCommand.php b/apps/backoffice/frontend/src/Command/ImportCoursesToElasticsearchCommand.php new file mode 100644 index 000000000..c27dd2396 --- /dev/null +++ b/apps/backoffice/frontend/src/Command/ImportCoursesToElasticsearchCommand.php @@ -0,0 +1,32 @@ +mySqlRepository->searchAll(); + + foreach ($courses as $course) { + $this->elasticRepository->save($course); + } + + return 0; + } +} diff --git a/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php b/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php new file mode 100644 index 000000000..a9604d1b2 --- /dev/null +++ b/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php @@ -0,0 +1,36 @@ +ask(new FindCoursesCounterQuery()); + + return $this->render( + 'pages/courses/courses.html.twig', + [ + 'title' => 'Courses', + 'description' => 'Courses CodelyTV - Backoffice', + 'courses_counter' => $coursesCounterResponse->total(), + 'new_course_id' => SimpleUuid::random()->value(), + ] + ); + } + + protected function exceptions(): array + { + return []; + } +} diff --git a/apps/backoffice/frontend/src/Controller/Courses/CoursesPostWebController.php b/apps/backoffice/frontend/src/Controller/Courses/CoursesPostWebController.php new file mode 100644 index 000000000..625d2a1d0 --- /dev/null +++ b/apps/backoffice/frontend/src/Controller/Courses/CoursesPostWebController.php @@ -0,0 +1,61 @@ +validateRequest($request); + + return $validationErrors->count() + ? $this->redirectWithErrors('courses_get', $validationErrors, $request) + : $this->createCourse($request); + } + + protected function exceptions(): array + { + return []; + } + + private function validateRequest(Request $request): ConstraintViolationListInterface + { + $constraint = new Assert\Collection( + [ + 'id' => new Assert\Uuid(), + 'name' => [new Assert\NotBlank(), new Assert\Length(['min' => 1, 'max' => 255])], + 'duration' => [new Assert\NotBlank(), new Assert\Length(['min' => 4, 'max' => 100])], + ] + ); + + $input = $request->request->all(); + + return Validation::createValidator()->validate($input, $constraint); + } + + private function createCourse(Request $request): RedirectResponse + { + $this->dispatch( + new CreateCourseCommand( + (string) $request->request->get('id'), + (string) $request->request->get('name'), + (string) $request->request->get('duration') + ) + ); + + return $this->redirectWithMessage( + 'courses_get', + sprintf('Feliciades, el curso %s ha sido creado!', $request->request->getAlpha('name')) + ); + } +} diff --git a/apps/backoffice/frontend/src/Controller/HealthCheck/HealthCheckGetController.php b/apps/backoffice/frontend/src/Controller/HealthCheck/HealthCheckGetController.php new file mode 100644 index 000000000..a38a47d39 --- /dev/null +++ b/apps/backoffice/frontend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -0,0 +1,20 @@ + 'ok', + ] + ); + } +} diff --git a/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php b/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php new file mode 100644 index 000000000..30e011847 --- /dev/null +++ b/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php @@ -0,0 +1,25 @@ +render('pages/home.html.twig', [ + 'title' => 'Welcome', + 'description' => 'CodelyTV - Backoffice', + ]); + } + + protected function exceptions(): array + { + return []; + } +} diff --git a/apps/backoffice/frontend/src/Controller/Metrics/MetricsController.php b/apps/backoffice/frontend/src/Controller/Metrics/MetricsController.php new file mode 100644 index 000000000..a10a699ea --- /dev/null +++ b/apps/backoffice/frontend/src/Controller/Metrics/MetricsController.php @@ -0,0 +1,23 @@ +render($this->monitor->registry()->getMetricFamilySamples()); + + return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); + } +} diff --git a/apps/backoffice/frontend/templates/master.html.twig b/apps/backoffice/frontend/templates/master.html.twig new file mode 100644 index 000000000..965f9ad4d --- /dev/null +++ b/apps/backoffice/frontend/templates/master.html.twig @@ -0,0 +1,34 @@ + + + + + + + + + {{ title }} + {{ description }} + + +{{ include('partials/header.html.twig') }} + +{% if flash.has('message') %} + +{% endif %} + +
+

{% block page_title %}{% endblock %}

+ {% block main %}{% endblock %} +
+ +
+{{ include('partials/footer.html.twig') }} + + + diff --git a/apps/backoffice/frontend/templates/pages/courses/courses.html.twig b/apps/backoffice/frontend/templates/pages/courses/courses.html.twig new file mode 100644 index 000000000..adfd9b003 --- /dev/null +++ b/apps/backoffice/frontend/templates/pages/courses/courses.html.twig @@ -0,0 +1,19 @@ +{% extends 'master.html.twig' %} + +{% block page_title %}Cursos{% endblock %} + +{% block main %} +
+ Sunset in the mountains +
+
Cursos
+

+ Actualmente CodelyTV Pro cuenta con {{ courses_counter }} cursos. +

+
+
+ {{ include('pages/courses/partials/new_course_form.html.twig') }} +
+
+ {{ include('pages/courses/partials/list_courses.html.twig') }} +{% endblock %} diff --git a/apps/backoffice/frontend/templates/pages/courses/partials/list_courses.html.twig b/apps/backoffice/frontend/templates/pages/courses/partials/list_courses.html.twig new file mode 100644 index 000000000..51b18eea1 --- /dev/null +++ b/apps/backoffice/frontend/templates/pages/courses/partials/list_courses.html.twig @@ -0,0 +1,153 @@ +

Cursos existentes

+ + +
+
+ +
+
+
+ + + +
+
+ + + + + + + + + + + +
+ Id + + Nombre + + DuraciΓ³n +
+ + + + diff --git a/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html.twig b/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html.twig new file mode 100644 index 000000000..3dab08075 --- /dev/null +++ b/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html.twig @@ -0,0 +1,48 @@ +
+

Crear curso

+
+
+ + + + {% if flash.has('errors.id') %} +

{{ flash.get('errors.id') }}

+ {% endif %} +
+
+
+
+ + + + {% if flash.has('errors.name') %} +

{{ flash.get('errors.name') }}

+ {% endif %} +
+
+ + + {% if flash.has('errors.duration') %} +

{{ flash.get('errors.duration') }}

+ {% endif %} +
+
+
+ +
+
diff --git a/apps/backoffice/frontend/templates/pages/home.html.twig b/apps/backoffice/frontend/templates/pages/home.html.twig new file mode 100644 index 000000000..3b6a5bcb1 --- /dev/null +++ b/apps/backoffice/frontend/templates/pages/home.html.twig @@ -0,0 +1,7 @@ +{% extends 'master.html.twig' %} + +{% block page_title %}HOME{% endblock %} + +{% block main %} + HOLIII HOME +{% endblock %} diff --git a/apps/backoffice/frontend/templates/partials/footer.html.twig b/apps/backoffice/frontend/templates/partials/footer.html.twig new file mode 100644 index 000000000..27a640fa6 --- /dev/null +++ b/apps/backoffice/frontend/templates/partials/footer.html.twig @@ -0,0 +1,7 @@ + diff --git a/apps/backoffice/frontend/templates/partials/header.html.twig b/apps/backoffice/frontend/templates/partials/header.html.twig new file mode 100644 index 000000000..c944f266b --- /dev/null +++ b/apps/backoffice/frontend/templates/partials/header.html.twig @@ -0,0 +1,28 @@ +
+ +
diff --git a/apps/backoffice/frontend/var/.gitkeep b/apps/backoffice/frontend/var/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/bootstrap.php b/apps/bootstrap.php new file mode 100644 index 000000000..8edbfc3b1 --- /dev/null +++ b/apps/bootstrap.php @@ -0,0 +1,17 @@ +loadEnv($rootPath . '/.env'); + +$_SERVER += $_ENV; +$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; +$_SERVER['APP_DEBUG'] ??= $_ENV['APP_DEBUG'] ?? $_SERVER['APP_ENV'] !== 'prod'; +$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = + (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/apps/mooc/backend/bin/console b/apps/mooc/backend/bin/console new file mode 100755 index 000000000..d013ee7a8 --- /dev/null +++ b/apps/mooc/backend/bin/console @@ -0,0 +1,42 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__) . '/../../bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new MoocBackendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$application = new Application($kernel); +$application->run($input); diff --git a/apps/mooc/backend/config/bundles.php b/apps/mooc/backend/config/bundles.php new file mode 100644 index 000000000..15c297c9f --- /dev/null +++ b/apps/mooc/backend/config/bundles.php @@ -0,0 +1,9 @@ + ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], + // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] +]; diff --git a/apps/mooc/backend/config/routes/courses.yaml b/apps/mooc/backend/config/routes/courses.yaml new file mode 100644 index 000000000..89d6e575b --- /dev/null +++ b/apps/mooc/backend/config/routes/courses.yaml @@ -0,0 +1,4 @@ +courses_put: + path: /courses/{id} + controller: CodelyTv\Apps\Mooc\Backend\Controller\Courses\CoursesPutController + methods: [PUT] diff --git a/apps/mooc/backend/config/routes/courses_counter.yaml b/apps/mooc/backend/config/routes/courses_counter.yaml new file mode 100644 index 000000000..8641b6d20 --- /dev/null +++ b/apps/mooc/backend/config/routes/courses_counter.yaml @@ -0,0 +1,4 @@ +courses_counter_get: + path: /courses-counter + controller: CodelyTv\Apps\Mooc\Backend\Controller\CoursesCounter\CoursesCounterGetController + methods: [GET] diff --git a/apps/mooc/backend/config/routes/health-check.yaml b/apps/mooc/backend/config/routes/health-check.yaml new file mode 100644 index 000000000..203ddbec5 --- /dev/null +++ b/apps/mooc/backend/config/routes/health-check.yaml @@ -0,0 +1,4 @@ +health-check_get: + path: /health-check + controller: CodelyTv\Apps\Mooc\Backend\Controller\HealthCheck\HealthCheckGetController + methods: [GET] diff --git a/apps/mooc/backend/config/routes/metrics.yaml b/apps/mooc/backend/config/routes/metrics.yaml new file mode 100644 index 000000000..d680b5046 --- /dev/null +++ b/apps/mooc/backend/config/routes/metrics.yaml @@ -0,0 +1,4 @@ +metrics_get: + path: /metrics + controller: CodelyTv\Apps\Mooc\Backend\Controller\Metrics\MetricsController + methods: [GET] diff --git a/apps/mooc/backend/config/services.yaml b/apps/mooc/backend/config/services.yaml new file mode 100644 index 000000000..7d81f0953 --- /dev/null +++ b/apps/mooc/backend/config/services.yaml @@ -0,0 +1,98 @@ +imports: + - { resource: ../../../../src/Mooc/Shared/Infrastructure/Symfony/DependencyInjection/mooc_services.yaml } + +services: + _defaults: + autoconfigure: true + autowire: true + + # Configure + _instanceof: + CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber: + tags: ['codely.domain_event_subscriber'] + + CodelyTv\Shared\Domain\Bus\Command\CommandHandler: + tags: ['codely.command_handler'] + + CodelyTv\Shared\Domain\Bus\Query\QueryHandler: + tags: ['codely.query_handler'] + + CodelyTv\Apps\Mooc\Backend\Controller\: + resource: '../src/Controller' + tags: ['controller.service_arguments'] + + CodelyTv\Apps\Mooc\Backend\Command\: + resource: '../src/Command' + tags: ['console.command'] + + # Wire + CodelyTv\Shared\: + resource: '../../../../src/Shared' + + CodelyTv\Mooc\: + resource: '../../../../src/Mooc' + + # -- TAGGING -- + CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus: + arguments: [!tagged codely.domain_event_subscriber] + lazy: true + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventMapping: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator: + arguments: [!tagged codely.domain_event_subscriber] + + CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections: + arguments: [!tagged codely.database_connection] + + CodelyTv\Shared\Infrastructure\Symfony\AddJsonBodyToRequestListener: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + + CodelyTv\Shared\Infrastructure\Symfony\ApiExceptionListener: + tags: + - { name: kernel.event_listener, event: kernel.exception, method: onException } + + + # -- APP DEFINITIONS -- + # Command/Query Handlers + CodelyTv\Shared\Infrastructure\Bus\Command\InMemorySymfonyCommandBus: + arguments: [!tagged codely.command_handler] + + CodelyTv\Shared\Infrastructure\Bus\Query\InMemorySymfonyQueryBus: + arguments: [!tagged codely.query_handler] + + # RabbitMQ + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection: + arguments: + - host: '%env(RABBITMQ_HOST)%' + port: '%env(RABBITMQ_PORT)%' + vhost: '%env(RABBITMQ_MOOC_VHOST)%' + login: '%env(RABBITMQ_LOGIN)%' + password: '%env(RABBITMQ_PASSWORD)%' + read_timeout: 2 + write_timeout: 2 + connect_timeout: 5 + + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection', '%env(RABBITMQ_EXCHANGE)%'] + + CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqDomainEventsConsumer: + arguments: + - '@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConnection' + - '@CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventJsonDeserializer' + - '%env(RABBITMQ_EXCHANGE)%' + - '%env(RABBITMQ_MAX_RETRIES)%' + + CodelyTv\Apps\Mooc\Backend\Command\DomainEvents\RabbitMq\ConfigureRabbitMqCommand: + arguments: + - '@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConfigurer' + - '%env(RABBITMQ_EXCHANGE)%' + - !tagged codely.domain_event_subscriber + + CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus: + arguments: ['@CodelyTv\Shared\Infrastructure\Monitoring\PrometheusMonitor', 'mooc_backend', '@CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqEventBus'] + + # -- IMPLEMENTATIONS SELECTOR -- + CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\WithMonitoring\WithPrometheusMonitoringEventBus' diff --git a/apps/mooc/backend/config/services/framework.yaml b/apps/mooc/backend/config/services/framework.yaml new file mode 100644 index 000000000..06fb3f3b4 --- /dev/null +++ b/apps/mooc/backend/config/services/framework.yaml @@ -0,0 +1,16 @@ +framework: + secret: '%env(APP_SECRET)%' + #csrf_protection: true + #http_method_override: true + + # Enables session support. Note that the session will ONLY be started if you read or write from it. + # Remove or comment this section to explicitly disable session support. + session: + handler_id: ~ + cookie_secure: auto + cookie_samesite: lax + + #esi: true + #fragments: true + php_errors: + log: true diff --git a/apps/mooc/backend/config/services_test.yaml b/apps/mooc/backend/config/services_test.yaml new file mode 100644 index 000000000..8e4fe2146 --- /dev/null +++ b/apps/mooc/backend/config/services_test.yaml @@ -0,0 +1,14 @@ +framework: + test: true + +services: + _defaults: + autoconfigure: true + autowire: true + + CodelyTv\Tests\: + resource: '../../../../tests' + + # Instance selector + CodelyTv\Shared\Domain\RandomNumberGenerator: '@CodelyTv\Tests\Shared\Infrastructure\ConstantRandomNumberGenerator' +# CodelyTv\Shared\Domain\Bus\Event\EventBus: '@CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus' diff --git a/apps/mooc/backend/public/index.php b/apps/mooc/backend/public/index.php new file mode 100644 index 000000000..b8655fe09 --- /dev/null +++ b/apps/mooc/backend/public/index.php @@ -0,0 +1,32 @@ +handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/apps/mooc/backend/src/Command/DomainEvents/MySql/ConsumeMySqlDomainEventsCommand.php b/apps/mooc/backend/src/Command/DomainEvents/MySql/ConsumeMySqlDomainEventsCommand.php new file mode 100644 index 000000000..16d96c2a6 --- /dev/null +++ b/apps/mooc/backend/src/Command/DomainEvents/MySql/ConsumeMySqlDomainEventsCommand.php @@ -0,0 +1,56 @@ +addArgument('quantity', InputArgument::REQUIRED, 'Quantity of events to process'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $quantityEventsToProcess = (int) $input->getArgument('quantity'); + + $consumer = pipe($this->consumer(), fn () => $this->connections->clear()); + + $this->consumer->consume($consumer, $quantityEventsToProcess); + + return 0; + } + + private function consumer(): callable + { + return function (DomainEvent $domainEvent): void { + $subscribers = $this->subscriberLocator->allSubscribedTo($domainEvent::class); + + foreach ($subscribers as $subscriber) { + $subscriber($domainEvent); + } + }; + } +} diff --git a/apps/mooc/backend/src/Command/DomainEvents/PublishDomainEventsFromMutationsCommand.php b/apps/mooc/backend/src/Command/DomainEvents/PublishDomainEventsFromMutationsCommand.php new file mode 100644 index 000000000..945784311 --- /dev/null +++ b/apps/mooc/backend/src/Command/DomainEvents/PublishDomainEventsFromMutationsCommand.php @@ -0,0 +1,91 @@ +transformers = [ + 'courses' => [ + DatabaseMutationAction::INSERT->value => DatabaseMutationToCourseCreatedDomainEvent::class, + DatabaseMutationAction::UPDATE->value => null, + DatabaseMutationAction::DELETE->value => null, + ], + ]; + } + + protected function configure(): void + { + $this->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of mutations to process'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $totalMutations = (int) $input->getArgument('quantity'); + + $this->entityManager->wrapInTransaction(function (EntityManager $entityManager) use ($totalMutations) { + $mutations = $entityManager->getConnection() + ->executeQuery("SELECT * FROM mutations ORDER BY id ASC LIMIT $totalMutations FOR UPDATE") + ->fetchAllAssociative(); + + foreach ($mutations as $mutation) { + $transformer = $this->findTransformer($mutation['table_name'], $mutation['operation']); + + if ($transformer === null) { + echo sprintf("Ignoring %s %s\n", $mutation['table_name'], $mutation['operation']); + continue; + } + + $domainEvents = $transformer->transform($mutation); + + $this->eventBus->publish(...$domainEvents); + } + + $entityManager->getConnection()->executeStatement( + sprintf('DELETE FROM mutations WHERE id IN (%s)', implode(',', array_column($mutations, 'id'))) + ); + }); + + return 0; + } + + private function findTransformer(string $tableName, string $operation): ?DatabaseMutationToDomainEvent + { + if (!array_key_exists($tableName, $this->transformers) && array_key_exists( + $operation, + $this->transformers[$tableName] + )) { + throw new RuntimeException("Transformer not found for table $tableName and operation $operation"); + } + + /** @var class-string|null $class */ + $class = $this->transformers[$tableName][$operation]; + + return $class ? new $class() : null; + } +} diff --git a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConfigureRabbitMqCommand.php b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConfigureRabbitMqCommand.php new file mode 100644 index 000000000..72801af71 --- /dev/null +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConfigureRabbitMqCommand.php @@ -0,0 +1,34 @@ +configurer->configure($this->exchangeName, ...iterator_to_array($this->subscribers)); + + return 0; + } +} diff --git a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConsumeRabbitMqDomainEventsCommand.php b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConsumeRabbitMqDomainEventsCommand.php new file mode 100644 index 000000000..c29c056d8 --- /dev/null +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConsumeRabbitMqDomainEventsCommand.php @@ -0,0 +1,59 @@ +addArgument('queue', InputArgument::REQUIRED, 'Queue name') + ->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of events to process'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $queueName = $input->getArgument('queue'); + $eventsToProcess = (int) $input->getArgument('quantity'); + + repeat($this->consumer($queueName), $eventsToProcess); + + return 0; + } + + private function consumer(string $queueName): callable + { + return function () use ($queueName): void { + $subscriber = $this->locator->withRabbitMqQueueNamed($queueName); + + $this->consumer->consume($subscriber, $queueName); + + $this->connections->clear(); + }; + } +} diff --git a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/GenerateSupervisorRabbitMqConsumerFilesCommand.php b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/GenerateSupervisorRabbitMqConsumerFilesCommand.php new file mode 100644 index 000000000..646392bf3 --- /dev/null +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/GenerateSupervisorRabbitMqConsumerFilesCommand.php @@ -0,0 +1,88 @@ +addArgument('command-path', InputArgument::OPTIONAL, 'Path on this is gonna be deployed', '/var/www'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $path = $input->getArgument('command-path'); + + each($this->configCreator($path), $this->locator->all()); + + return 0; + } + + private function configCreator(string $path): callable + { + return function (DomainEventSubscriber $subscriber) use ($path): void { + $queueName = RabbitMqQueueNameFormatter::format($subscriber); + $subscriberName = RabbitMqQueueNameFormatter::shortFormat($subscriber); + + $fileContent = str_replace( + ['{subscriber_name}', '{queue_name}', '{path}', '{processes}', '{events_to_process}', ], + [ + $subscriberName, + $queueName, + $path, + self::NUMBERS_OF_PROCESSES_PER_SUBSCRIBER, + self::EVENTS_TO_PROCESS_AT_TIME, + ], + $this->template() + ); + + file_put_contents($this->fileName($subscriberName), $fileContent); + }; + } + + private function template(): string + { + return <<dispatch( + new CreateCourseCommand( + $id, + (string) $request->request->get('name'), + (string) $request->request->get('duration') + ) + ); + + return new Response('', Response::HTTP_CREATED); + } + + protected function exceptions(): array + { + return []; + } +} diff --git a/apps/mooc/backend/src/Controller/CoursesCounter/CoursesCounterGetController.php b/apps/mooc/backend/src/Controller/CoursesCounter/CoursesCounterGetController.php new file mode 100644 index 000000000..0fb94b6ff --- /dev/null +++ b/apps/mooc/backend/src/Controller/CoursesCounter/CoursesCounterGetController.php @@ -0,0 +1,34 @@ +ask(new FindCoursesCounterQuery()); + + return new JsonResponse( + [ + 'total' => $response->total(), + ] + ); + } + + protected function exceptions(): array + { + return [ + CoursesCounterNotExist::class => Response::HTTP_NOT_FOUND, + ]; + } +} diff --git a/apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php b/apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php new file mode 100644 index 000000000..1c20feae2 --- /dev/null +++ b/apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -0,0 +1,24 @@ + 'ok', + 'rand' => $this->generator->generate(), + ] + ); + } +} diff --git a/apps/mooc/backend/src/Controller/Metrics/MetricsController.php b/apps/mooc/backend/src/Controller/Metrics/MetricsController.php new file mode 100644 index 000000000..9db1cebc2 --- /dev/null +++ b/apps/mooc/backend/src/Controller/Metrics/MetricsController.php @@ -0,0 +1,23 @@ +render($this->monitor->registry()->getMetricFamilySamples()); + + return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); + } +} diff --git a/apps/mooc/backend/src/MoocBackendKernel.php b/apps/mooc/backend/src/MoocBackendKernel.php new file mode 100644 index 000000000..e0b82d39e --- /dev/null +++ b/apps/mooc/backend/src/MoocBackendKernel.php @@ -0,0 +1,46 @@ +getProjectDir() . '/config/bundles.php'; + foreach ($contents as $class => $envs) { + if ($envs[$this->environment] ?? $envs['all'] ?? false) { + yield new $class(); + } + } + } + + public function getProjectDir(): string + { + return dirname(__DIR__); + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + { + $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); + $container->setParameter('.container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir() . '/config'; + + $loader->load($confDir . '/services' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services_' . $this->environment . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/services/*' . self::CONFIG_EXTS, 'glob'); + } +} diff --git a/apps/mooc/backend/tests/features/courses/course_put.feature b/apps/mooc/backend/tests/features/courses/course_put.feature new file mode 100644 index 000000000..803bc2a5d --- /dev/null +++ b/apps/mooc/backend/tests/features/courses/course_put.feature @@ -0,0 +1,15 @@ +Feature: Create a new course + In order to have courses on the platform + As a user with admin permissions + I want to create a new course + + Scenario: A valid non existing course + Given I send a PUT request to "/courses/1aab45ba-3c7a-4344-8936-78466eca77fa" with body: + """ + { + "name": "The best course", + "duration": "5 hours" + } + """ + Then the response status code should be 201 + And the response should be empty diff --git a/apps/mooc/backend/tests/features/courses_counter/courses_counter_get.feature b/apps/mooc/backend/tests/features/courses_counter/courses_counter_get.feature new file mode 100644 index 000000000..13719be7f --- /dev/null +++ b/apps/mooc/backend/tests/features/courses_counter/courses_counter_get.feature @@ -0,0 +1,96 @@ +Feature: Obtain the total number of courses + In order to have a courses counter + As a user + I want to see the courses counter + + Scenario: With one course + Given I send an event to the event bus: + """ + { + "data": { + "id": "c77fa036-cbc7-4414-996b-c6a7a93cae09", + "type": "course.created", + "occurred_on": "2019-08-08T08:37:32+00:00", + "attributes": { + "id": "8c900b20-e04a-4777-9183-32faab6d2fb5", + "name": "DDD en PHP!", + "duration": "25 hours" + }, + "meta" : { + "host": "111.26.06.93" + } + } + } + """ + When I send a "GET" request to "/courses-counter" + Then the response status code should be 200 + And the response content should be: + """ + { + "total": 1 + } + """ + + Scenario: With more than one course having duplicates + Given I send an event to the event bus: + """ + { + "data": { + "id": "c77fa036-cbc7-4414-996b-c6a7a93cae09", + "type": "course.created", + "occurred_on": "2019-08-08T08:37:32+00:00", + "attributes": { + "id": "8c900b20-e04a-4777-9183-32faab6d2fb5", + "name": "DDD en PHP!", + "duration": "25 hours" + }, + "meta" : { + "host": "111.26.06.93" + } + } + } + """ + And I send an event to the event bus: + """ + { + "data": { + "id": "8c4a4ed8-9458-489e-a167-b099d81fa096", + "type": "course.created", + "occurred_on": "2019-08-09T08:36:32+00:00", + "attributes": { + "id": "8c4a4ed8-9458-489e-a167-b099d81fa096", + "name": "DDD en Java", + "duration": "24 hours" + }, + "meta" : { + "host": "111.26.06.93" + } + } + } + """ + And I send an event to the event bus: + """ + { + "data": { + "id": "8c4a4ed8-9458-489e-a167-b099d81fa096", + "type": "course.created", + "occurred_on": "2019-08-09T08:36:32+00:00", + "attributes": { + "id": "8c4a4ed8-9458-489e-a167-b099d81fa096", + "name": "DDD en Java", + "duration": "24 hours" + }, + "meta" : { + "host": "111.26.06.93" + } + } + } + """ + When I send a "GET" request to "/courses-counter" + Then the response status code should be 200 + And the response content should be: + """ + { + "total": 2 + } + """ diff --git a/tests/applications/mooc_backend/features/status/status_get.feature b/apps/mooc/backend/tests/features/health_check/health_check_get.feature similarity index 70% rename from tests/applications/mooc_backend/features/status/status_get.feature rename to apps/mooc/backend/tests/features/health_check/health_check_get.feature index 1e05acd5e..96f4c0e75 100644 --- a/tests/applications/mooc_backend/features/status/status_get.feature +++ b/apps/mooc/backend/tests/features/health_check/health_check_get.feature @@ -4,10 +4,11 @@ Feature: Api status I want to check the api status Scenario: Check the api status - Given I send a GET request to "/status" + Given I send a GET request to "/health-check" Then the response content should be: """ { - "status": "OK" + "mooc-backend": "ok", + "rand": 1 } """ diff --git a/apps/mooc/backend/tests/mooc_backend.yml b/apps/mooc/backend/tests/mooc_backend.yml new file mode 100644 index 000000000..75681594e --- /dev/null +++ b/apps/mooc/backend/tests/mooc_backend.yml @@ -0,0 +1,29 @@ +mooc_backend: + extensions: + FriendsOfBehat\SymfonyExtension: + kernel: + class: CodelyTv\Apps\Mooc\Backend\MoocBackendKernel + bootstrap: apps/bootstrap.php + Behat\MinkExtension: + sessions: + symfony: + symfony: ~ + base_url: '' + + suites: + health_check: + paths: [ apps/mooc/backend/tests/features/health_check ] + contexts: + - CodelyTv\Tests\Shared\Infrastructure\Behat\ApiContext + + courses: + paths: [ apps/mooc/backend/tests/features/courses ] + contexts: + - CodelyTv\Tests\Shared\Infrastructure\Behat\ApplicationFeatureContext + - CodelyTv\Tests\Shared\Infrastructure\Behat\ApiContext + + courses_counter: + paths: [ apps/mooc/backend/tests/features/courses_counter ] + contexts: + - CodelyTv\Tests\Shared\Infrastructure\Behat\ApplicationFeatureContext + - CodelyTv\Tests\Shared\Infrastructure\Behat\ApiContext diff --git a/apps/mooc/backend/var/.gitkeep b/apps/mooc/backend/var/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/mooc/frontend/src/.gitkeep b/apps/mooc/frontend/src/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/mooc/frontend/var/.gitkeep b/apps/mooc/frontend/var/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/behat.yml b/behat.yml new file mode 100644 index 000000000..af66723dd --- /dev/null +++ b/behat.yml @@ -0,0 +1,2 @@ +imports: + - apps/mooc/backend/tests/mooc_backend.yml diff --git a/behat.yml.dist b/behat.yml.dist deleted file mode 100644 index 994de435a..000000000 --- a/behat.yml.dist +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - tests/applications/mooc_backend/mooc_backend.yml diff --git a/composer.json b/composer.json index df19dbe51..52f696576 100644 --- a/composer.json +++ b/composer.json @@ -1,93 +1,88 @@ { - "name": "codelytv/cqrs-ddd-example", + "name": "codelytv/php-ddd-example", "license": "MIT", "type": "project", - "description": "A simple skeleton for CQRS code :)", + "description": "An example project applying Domain-Driven Design, Hexagonal Architecture and CQRS in a Monorepository", "require": { - "php": "7.3.6", + "php": "^8.3", "ext-amqp": "*", "ext-apcu": "*", - "ext-bcmath": "*", - "ext-ctype": "*", - "ext-intl": "*", "ext-json": "*", "ext-zend-opcache": "*", "ext-pdo": "*", - "incenteev/composer-parameter-handler": "^2.1", + "symfony/framework-bundle": "^7", + "symfony/messenger": "^7", + "symfony/dotenv": "^7", + "symfony/yaml": "^7", + "symfony/twig-bundle": "^7", + "symfony/validator": "^7", + "symfony/cache": "^7", - "symfony/framework-bundle": "^4.2", - "symfony/console": "^4.2", - "symfony/process": "^4.2", - "symfony/messenger": "^4.2", - "symfony/monolog-bundle": "^3.3", - "symfony/dotenv": "^4.2", + "lambdish/phunctional": "^2", - "friendsofsymfony/rest-bundle": "^2.4", - "jms/serializer-bundle": "^2.4", - "symfony/twig-bundle": "^4.2", + "ramsey/uuid": "^4", - "ocramius/proxy-manager": "2.2.*", + "doctrine/dbal": "^3", + "doctrine/orm": "^2", - "doctrine/dbal": "^2.8", - "doctrine/orm": "^2.6", + "ocramius/proxy-manager": "^2", + "laminas/laminas-zendframework-bridge": "^1", - "lambdish/phunctional": "^1.0", + "elasticsearch/elasticsearch": "^7", + "monolog/monolog": "^3", - "swiftmailer/swiftmailer": "^5.4", - "maknz/slack": "^1.7", - - "ramsey/uuid": "^3.8" + "promphp/prometheus_client_php": "^2.7.2" }, "require-dev": { "ext-xdebug": "*", "roave/security-advisories": "dev-master", - "phpunit/phpunit": "^6.5", - "symfony/phpunit-bridge": "^4.2", - "mockery/mockery": "^1.0", - "fzaninotto/faker": "^1.7", - "squizlabs/php_codesniffer": "^2.9", + "behat/behat": "^3.13", + "friends-of-behat/mink-extension": "2.7.5", + "friends-of-behat/symfony-extension": "2.6.0", + "behat/mink-browserkit-driver": "2.2.0", - "behat/behat": "^3.4", - "behat/mink-extension": "^2.3", - "behat/mink-browserkit-driver": "^1.3", - "friends-of-behat/symfony-extension": "^2.0", + "phpunit/phpunit": "^9", + "mockery/mockery": "^1", - "phpstan/phpstan": "^0.10.3", - "phpstan/phpstan-mockery": "^0.10.2" - }, - "scripts": { - "post-install-cmd": ["Incenteev\\ParameterHandler\\ScriptHandler::buildParameters"], - "post-update-cmd": ["Incenteev\\ParameterHandler\\ScriptHandler::buildParameters"], - "behat": "behat -p api && behat -p applications", - "phpunit": "phpunit --exclude-group='disabled'", - "phpstan": "phpstan analyse -l 7 -c etc/phpstan/phpstan.neon applications/api/src applications/codely/src --quiet", - "test": ["@phpstan", "@phpunit", "@behat"] - }, - "extra": { - "incenteev-parameters": [ - { "file": "src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_parameters.yml"}, - { "file": "src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_parameters.yml"} - ] + "fakerphp/faker": "^1", + + "symfony/error-handler": "^7", + + "symplify/easy-coding-standard": "^12.0", + "vimeo/psalm": "^5.15", + "rector/rector": "^0.18.12", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-symfony": "^5.0", + "psalm/plugin-phpunit": "^0.18.4", + "phpstan/phpstan": "^1.10", + "phpat/phpat": "^0.10.10", + "phpmd/phpmd": "^2.14", + "codelytv/coding-style": "^1.1" }, "autoload": { "psr-4": { - "CodelyTv\\MoocBackend\\": "applications/mooc_backend/src", + "CodelyTv\\Apps\\Mooc\\Backend\\": "apps/mooc/backend/src", + "CodelyTv\\Apps\\Mooc\\Frontend\\": "apps/mooc/frontend/src", + + "CodelyTv\\Apps\\Backoffice\\Backend\\": "apps/backoffice/backend/src", + "CodelyTv\\Apps\\Backoffice\\Frontend\\": "apps/backoffice/frontend/src", + "CodelyTv\\": ["src"] - }, - "files": [ - "src/Shared/utils.php" - ] + } }, "autoload-dev": { "psr-4": { - "CodelyTv\\Test\\": "tests/src" - }, - "files": [ - "tests/src/Shared/utils.php" - ] + "CodelyTv\\Tests\\": ["tests"] + } + }, + "minimum-stability": "RC", + "config": { + "allow-plugins": { + "ocramius/package-versions": true + } } } diff --git a/composer.lock b/composer.lock index f34be2433..534d648f6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,112 +4,97 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b6fd292758a9cc7b516ef0bbeb0f5928", + "content-hash": "1384ca0a67984f7a0296f15a4373fed1", "packages": [ { - "name": "doctrine/annotations", - "version": "v1.6.1", + "name": "brick/math", + "version": "0.12.1", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24" + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/53120e0eb10355388d6ccbe462f1fea34ddadb24", - "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" + "php": "^8.1" }, "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "Brick\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "description": "Arbitrary-precision arithmetic library", "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2019-03-25T19:12:02+00:00" + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" }, { "name": "doctrine/cache", - "version": "v1.8.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", "shasum": "" }, "require": { - "php": "~7.1" + "php": "~7.1 || ^8.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^4.0", - "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0", - "predis/predis": "~1.0" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" @@ -120,6 +105,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -128,10 +117,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -141,46 +126,69 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "https://www.doctrine-project.org", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ + "abstraction", + "apcu", "cache", - "caching" + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" ], - "time": "2018-08-21T18:01:43+00:00" + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" }, { "name": "doctrine/collections", - "version": "v1.6.1", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956" + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/d2ae4ef05e25197343b6a39bae1d3c427a2f6956", - "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956", + "url": "https://api.github.com/repos/doctrine/collections/zipball/d8af7f248c74f195f7347424600fd9e17b57af59", + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59", "shasum": "" }, "require": { - "php": "^7.1.3" + "doctrine/deprecations": "^1", + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpstan/phpstan-shim": "^0.9.2", - "phpunit/phpunit": "^7.0", - "vimeo/psalm": "^3.2.2" + "doctrine/coding-standard": "^12", + "ext-json": "*", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" + "Doctrine\\Common\\Collections\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -188,6 +196,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -196,10 +208,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -217,48 +225,58 @@ "iterators", "php" ], - "time": "2019-03-25T19:03:48+00:00" + "support": { + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.2.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" + } + ], + "time": "2024-04-18T06:56:21+00:00" }, { "name": "doctrine/common", - "version": "v2.10.0", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "30e33f60f64deec87df728c02b107f82cdafad9d" + "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/30e33f60f64deec87df728c02b107f82cdafad9d", - "reference": "30e33f60f64deec87df728c02b107f82cdafad9d", + "url": "https://api.github.com/repos/doctrine/common/zipball/0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", + "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", "shasum": "" }, "require": { - "doctrine/annotations": "^1.0", - "doctrine/cache": "^1.0", - "doctrine/collections": "^1.0", - "doctrine/event-manager": "^1.0", - "doctrine/inflector": "^1.0", - "doctrine/lexer": "^1.0", - "doctrine/persistence": "^1.1", - "doctrine/reflection": "^1.0", - "php": "^7.1" + "doctrine/persistence": "^2.0 || ^3.0", + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^1.0", - "phpunit/phpunit": "^6.3", + "doctrine/coding-standard": "^9.0 || ^10.0", + "doctrine/collections": "^1", + "phpstan/phpstan": "^1.4.1", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", "squizlabs/php_codesniffer": "^3.0", - "symfony/phpunit-bridge": "^4.0.5" + "symfony/phpunit-bridge": "^6.1", + "vimeo/psalm": "^4.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.10.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -266,6 +284,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -274,10 +296,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -291,42 +309,69 @@ "email": "ocramius@gmail.com" } ], - "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, persistence interfaces, proxies, event system and much more.", + "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", "homepage": "https://www.doctrine-project.org/projects/common.html", "keywords": [ "common", "doctrine", "php" ], - "time": "2018-11-21T01:24:55+00:00" + "support": { + "issues": "https://github.com/doctrine/common/issues", + "source": "https://github.com/doctrine/common/tree/3.4.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon", + "type": "tidelift" + } + ], + "time": "2024-04-16T13:35:33+00:00" }, { "name": "doctrine/dbal", - "version": "v2.9.2", + "version": "3.8.6", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9" + "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9", - "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/b7411825cf7efb7e51f9791dea19d86e43b399a1", + "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1", "shasum": "" }, "require": { - "doctrine/cache": "^1.0", - "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "^5.0", - "jetbrains/phpstorm-stubs": "^2018.1.2", - "phpstan/phpstan": "^0.10.1", - "phpunit/phpunit": "^7.4", - "symfony/console": "^2.0.5|^3.0|^4.0", - "symfony/phpunit-bridge": "^3.4.5|^4.0.5" + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.11.5", + "phpstan/phpstan-strict-rules": "^1.6", + "phpunit/phpunit": "9.6.19", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.10.1", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/console": "^4.4|^5.4|^6.0|^7.0", + "vimeo/psalm": "4.30.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -335,15 +380,9 @@ "bin/doctrine-dbal" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "3.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -351,6 +390,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -359,10 +402,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -373,48 +412,120 @@ "keywords": [ "abstraction", "database", + "db2", "dbal", + "mariadb", + "mssql", "mysql", - "persistence", + "oci8", + "oracle", + "pdo", "pgsql", - "php", - "queryobject" + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.8.6" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2024-06-19T10:38:17+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2018-12-31T03:27:51+00:00" + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/event-manager", - "version": "v1.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^8.1" }, "conflict": { - "doctrine/common": "<2.9@dev" + "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^4.0", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.24" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -422,6 +533,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -430,10 +545,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -447,44 +558,64 @@ "email": "ocramius@gmail.com" } ], - "description": "Doctrine Event Manager component", + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ "event", - "eventdispatcher", - "eventmanager" + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } ], - "time": "2018-06-11T11:59:03+00:00" + "time": "2024-05-22T20:47:39+00:00" }, { "name": "doctrine/inflector", - "version": "v1.3.0", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -492,6 +623,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -500,10 +635,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -513,48 +644,68 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ "inflection", - "pluralize", - "singularize", - "string" + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } ], - "time": "2018-01-09T20:05:19+00:00" + "time": "2024-02-18T20:23:39+00:00" }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -568,7 +719,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", @@ -577,34 +728,54 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "v1.0.1", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^8.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, + "type": "library", "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -612,73 +783,109 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ + "annotations", + "docblock", "lexer", - "parser" + "parser", + "php" ], - "time": "2014-09-09T13:34:57+00:00" + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" }, { "name": "doctrine/orm", - "version": "v2.6.3", + "version": "2.19.6", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "434820973cadf2da2d66e7184be370084cc32ca8" + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/434820973cadf2da2d66e7184be370084cc32ca8", - "reference": "434820973cadf2da2d66e7184be370084cc32ca8", + "url": "https://api.github.com/repos/doctrine/orm/zipball/c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", "shasum": "" }, "require": { - "doctrine/annotations": "~1.5", - "doctrine/cache": "~1.6", - "doctrine/collections": "^1.4", - "doctrine/common": "^2.7.1", - "doctrine/dbal": "^2.6", - "doctrine/instantiator": "~1.1", - "ext-pdo": "*", - "php": "^7.1", - "symfony/console": "~3.0|~4.0" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.12.1 || ^2.1.1", + "doctrine/collections": "^1.5 || ^2.1", + "doctrine/common": "^3.0.3", + "doctrine/dbal": "^2.13.1 || ^3.2", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2", + "doctrine/inflector": "^1.4 || ^2.0", + "doctrine/instantiator": "^1.3 || ^2", + "doctrine/lexer": "^2 || ^3", + "doctrine/persistence": "^2.4 || ^3", + "ext-ctype": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3", + "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/polyfill-php72": "^1.23", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.13 || >= 3.0" }, "require-dev": { - "doctrine/coding-standard": "^1.0", - "phpunit/phpunit": "^6.5", - "squizlabs/php_codesniffer": "^3.2", - "symfony/yaml": "~3.4|~4.0" + "doctrine/annotations": "^1.13 || ^2", + "doctrine/coding-standard": "^9.0.2 || ^12.0", + "phpbench/phpbench": "^0.16.10 || ^1.0", + "phpstan/phpstan": "~1.4.10 || 1.11.1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "psr/log": "^1 || ^2 || ^3", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", + "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "4.30.0 || 5.24.0" }, "suggest": { + "ext-dom": "Provides support for XSD validation for XML mapping files", + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" }, "bin": [ "bin/doctrine" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\ORM\\": "lib/Doctrine/ORM" + "Doctrine\\ORM\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -686,6 +893,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -694,10 +905,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -708,52 +915,53 @@ } ], "description": "Object-Relational-Mapper for PHP", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/orm.html", "keywords": [ "database", "orm" ], - "time": "2018-11-20T23:46:46+00:00" + "support": { + "issues": "https://github.com/doctrine/orm/issues", + "source": "https://github.com/doctrine/orm/tree/2.19.6" + }, + "time": "2024-06-26T17:24:40+00:00" }, { "name": "doctrine/persistence", - "version": "v1.1.0", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "c0f1c17602afc18b4cbd8e1c8125f264c9cf7d38" + "reference": "b337726451f5d530df338fc7f68dee8781b49779" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/c0f1c17602afc18b4cbd8e1c8125f264c9cf7d38", - "reference": "c0f1c17602afc18b4cbd8e1c8125f264c9cf7d38", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/b337726451f5d530df338fc7f68dee8781b49779", + "reference": "b337726451f5d530df338fc7f68dee8781b49779", "shasum": "" }, "require": { - "doctrine/annotations": "^1.0", - "doctrine/cache": "^1.0", - "doctrine/collections": "^1.0", - "doctrine/event-manager": "^1.0", - "doctrine/reflection": "^1.0", - "php": "^7.1" + "doctrine/event-manager": "^1 || ^2", + "php": "^7.2 || ^8.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0" }, "conflict": { - "doctrine/common": "<2.10@dev" + "doctrine/common": "<2.10" }, "require-dev": { - "doctrine/coding-standard": "^5.0", - "phpstan/phpstan": "^0.8", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^12", + "doctrine/common": "^3.0", + "phpstan/phpstan": "1.11.1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6.0", + "vimeo/psalm": "4.30.0 || 5.24.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Persistence\\": "src/Persistence" } }, "notification-url": "https://packagist.org/downloads/", @@ -761,6 +969,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -769,10 +981,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -787,7 +995,7 @@ } ], "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", - "homepage": "https://doctrine-project.org/projects/persistence.html", + "homepage": "https://www.doctrine-project.org/projects/persistence.html", "keywords": [ "mapper", "object", @@ -795,225 +1003,122 @@ "orm", "persistence" ], - "time": "2018-11-21T00:33:13+00:00" - }, - { - "name": "doctrine/reflection", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/reflection.git", - "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/reflection/zipball/02538d3f95e88eb397a5f86274deb2c6175c2ab6", - "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "^4.0", - "doctrine/common": "^2.8", - "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4", - "phpunit/phpunit": "^7.0", - "squizlabs/php_codesniffer": "^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" - } + "support": { + "issues": "https://github.com/doctrine/persistence/issues", + "source": "https://github.com/doctrine/persistence/tree/3.3.3" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence", + "type": "tidelift" } ], - "description": "Doctrine Reflection component", - "homepage": "https://www.doctrine-project.org/projects/reflection.html", - "keywords": [ - "reflection" - ], - "time": "2018-06-14T14:45:07+00:00" + "time": "2024-06-20T10:14:30+00:00" }, { - "name": "friendsofsymfony/rest-bundle", - "version": "2.5.0", + "name": "elasticsearch/elasticsearch", + "version": "v7.17.2", "source": { "type": "git", - "url": "https://github.com/FriendsOfSymfony/FOSRestBundle.git", - "reference": "a5fc73b84bdb2f0fdf58a717b322ceb6997f7bf3" + "url": "https://github.com/elastic/elasticsearch-php.git", + "reference": "2d302233f2bb0926812d82823bb820d405e130fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRestBundle/zipball/a5fc73b84bdb2f0fdf58a717b322ceb6997f7bf3", - "reference": "a5fc73b84bdb2f0fdf58a717b322ceb6997f7bf3", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/2d302233f2bb0926812d82823bb820d405e130fc", + "reference": "2d302233f2bb0926812d82823bb820d405e130fc", "shasum": "" }, "require": { - "doctrine/inflector": "^1.0", - "php": "^5.5.9|~7.0", - "psr/log": "^1.0", - "symfony/config": "^3.4|^4.0", - "symfony/debug": "^3.4|^4.0", - "symfony/dependency-injection": "^3.4|^4.0", - "symfony/event-dispatcher": "^3.4|^4.0", - "symfony/finder": "^3.4|^4.0", - "symfony/framework-bundle": "^3.4|^4.0", - "symfony/http-foundation": "^3.4|^4.0", - "symfony/http-kernel": "^3.4|^4.0", - "symfony/routing": "^3.4|^4.0", - "symfony/security-core": "^3.4|^4.0", - "symfony/templating": "^3.4|^4.0", - "willdurand/jsonp-callback-validator": "^1.0", - "willdurand/negotiation": "^2.0" - }, - "conflict": { - "jms/serializer": "<1.13.0", - "jms/serializer-bundle": "<2.0.0", - "sensio/framework-extra-bundle": "<3.0.13" + "ext-json": ">=1.3.7", + "ezimuel/ringphp": "^1.1.2", + "php": "^7.3 || ^8.0", + "psr/log": "^1|^2|^3" }, "require-dev": { - "jms/serializer": "^1.13|^2.0", - "jms/serializer-bundle": "^2.3.1|^3.0", - "phpoption/phpoption": "^1.1", - "psr/http-message": "^1.0", - "sensio/framework-extra-bundle": "^3.0.13|^4.0|^5.0", - "symfony/asset": "^3.4|^4.0", - "symfony/browser-kit": "^3.4|^4.0", - "symfony/css-selector": "^3.4|^4.0", - "symfony/dependency-injection": "^2.7.20|^3.0|^4.0", - "symfony/expression-language": "~2.7|^3.0|^4.0", - "symfony/form": "^3.4|^4.0", - "symfony/phpunit-bridge": "^4.1.8", - "symfony/security-bundle": "^3.4|^4.0", - "symfony/serializer": "^2.7.11|^3.0.4|^4.0", - "symfony/twig-bundle": "^3.4|^4.0", - "symfony/validator": "^3.4|^4.0", - "symfony/web-profiler-bundle": "^3.4|^4.0", - "symfony/yaml": "^3.4|^4.0" + "ext-yaml": "*", + "ext-zip": "*", + "mockery/mockery": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.4", + "symfony/finder": "~4.0" }, "suggest": { - "jms/serializer-bundle": "Add support for advanced serialization capabilities, recommended, requires ^2.0|^3.0", - "sensio/framework-extra-bundle": "Add support for the request body converter and the view response listener, requires ^3.0", - "symfony/expression-language": "Add support for using the expression language in the routing, requires ^2.7|^3.0", - "symfony/serializer": "Add support for basic serialization capabilities and xml decoding, requires ^2.7|^3.0", - "symfony/validator": "Add support for validation capabilities in the ParamFetcher, requires ^2.7|^3.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } + "ext-curl": "*", + "monolog/monolog": "Allows for client-level logging and tracing" }, + "type": "library", "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "FOS\\RestBundle\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] + "Elasticsearch\\": "src/Elasticsearch/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0", + "LGPL-2.1-only" ], "authors": [ { - "name": "Lukas Kahwe Smith", - "email": "smith@pooteeweet.org" - }, - { - "name": "FriendsOfSymfony Community", - "homepage": "https://github.com/friendsofsymfony/FOSRestBundle/contributors" + "name": "Zachary Tong" }, { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com" + "name": "Enrico Zimuel" } ], - "description": "This Bundle provides various tools to rapidly develop RESTful API's with Symfony", - "homepage": "http://friendsofsymfony.github.com", + "description": "PHP Client for Elasticsearch", "keywords": [ - "rest" + "client", + "elasticsearch", + "search" ], - "time": "2019-01-03T13:05:12+00:00" + "support": { + "issues": "https://github.com/elastic/elasticsearch-php/issues", + "source": "https://github.com/elastic/elasticsearch-php/tree/v7.17.2" + }, + "time": "2023-04-21T15:31:12+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "name": "ezimuel/guzzlestreams", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "url": "https://github.com/ezimuel/guzzlestreams.git", + "reference": "b4b5a025dfee70d6cd34c780e07330eb93d5b997" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/ezimuel/guzzlestreams/zipball/b4b5a025dfee70d6cd34c780e07330eb93d5b997", + "reference": "b4b5a025dfee70d6cd34c780e07330eb93d5b997", "shasum": "" }, "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" + "php": ">=5.4.0" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" + "phpunit/phpunit": "~9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\": "src/" + "GuzzleHttp\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1027,52 +1132,56 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Guzzle is a PHP HTTP client library", + "description": "Fork of guzzle/streams (abandoned) to be used with elasticsearch-php", "homepage": "http://guzzlephp.org/", "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" + "Guzzle", + "stream" ], - "time": "2018-04-22T15:46:56+00:00" + "support": { + "source": "https://github.com/ezimuel/guzzlestreams/tree/3.1.0" + }, + "time": "2022-10-24T12:58:50+00:00" }, { - "name": "guzzlehttp/promises", - "version": "v1.3.1", + "name": "ezimuel/ringphp", + "version": "1.2.2", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + "url": "https://github.com/ezimuel/ringphp.git", + "reference": "7887fc8488013065f72f977dcb281994f5fde9f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "url": "https://api.github.com/repos/ezimuel/ringphp/zipball/7887fc8488013065f72f977dcb281994f5fde9f4", + "reference": "7887fc8488013065f72f977dcb281994f5fde9f4", "shasum": "" }, "require": { - "php": ">=5.5.0" + "ezimuel/guzzlestreams": "^3.0.1", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "replace": { + "guzzlehttp/ringphp": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "ext-curl": "*", + "phpunit/phpunit": "~9.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "GuzzleHttp\\Ring\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1085,49 +1194,37 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "time": "2016-12-20T10:07:11+00:00" + "description": "Fork of guzzle/RingPHP (abandoned) to be used with elasticsearch-php", + "support": { + "source": "https://github.com/ezimuel/ringphp/tree/1.2.2" + }, + "time": "2022-12-07T11:28:53+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "1.5.2", + "name": "lambdish/phunctional", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "9f83dded91781a01c63574e387eaa769be769115" + "url": "https://github.com/Lambdish/phunctional.git", + "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", - "reference": "9f83dded91781a01c63574e387eaa769be769115", + "url": "https://api.github.com/repos/Lambdish/phunctional/zipball/ed3482e7da134d886789abb33c6df22a5d2f271c", + "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5" - }, - "provide": { - "psr/http-message-implementation": "1.0" + "php": ">=7.2" }, "require-dev": { - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + "phpstan/phpstan": "^0.11.16", + "phpunit/phpunit": "^8.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, "files": [ - "src/functions_include.php" + "src/_bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1136,226 +1233,299 @@ ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Eloi Poch" + }, + { + "name": "Jorge Ávila" }, { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" + "name": "Rafa GΓ³mez" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "Ξ» PHP functional library", "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" + "functional", + "generator", + "lambda", + "library", + "php" ], - "time": "2018-12-04T20:46:45+00:00" + "support": { + "issues": "https://github.com/Lambdish/phunctional/issues", + "source": "https://github.com/Lambdish/phunctional/tree/v2.1.0" + }, + "time": "2020-09-18T07:22:08+00:00" }, { - "name": "incenteev/composer-parameter-handler", - "version": "v2.1.3", + "name": "laminas/laminas-code", + "version": "4.14.0", "source": { "type": "git", - "url": "https://github.com/Incenteev/ParameterHandler.git", - "reference": "933c45a34814f27f2345c11c37d46b3ca7303550" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/933c45a34814f27f2345c11c37d46b3ca7303550", - "reference": "933c45a34814f27f2345c11c37d46b3ca7303550", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/562e02b7d85cb9142b5116cc76c4c7c162a11a1c", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c", "shasum": "" }, "require": { - "php": ">=5.3.3", - "symfony/yaml": "^2.3 || ^3.0 || ^4.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { - "composer/composer": "^1.0@dev", - "symfony/filesystem": "^2.3 || ^3 || ^4", - "symfony/phpunit-bridge": "^4.0" + "doctrine/annotations": "^2.0.1", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^2.5.0", + "laminas/laminas-stdlib": "^3.17.0", + "phpunit/phpunit": "^10.3.3", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.15.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, + "type": "library", "autoload": { "psr-4": { - "Incenteev\\ParameterHandler\\": "" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", + "keywords": [ + "code", + "laminas", + "laminasframework" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-code/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-code/issues", + "rss": "https://github.com/laminas/laminas-code/releases.atom", + "source": "https://github.com/laminas/laminas-code" + }, + "funding": [ { - "name": "Christophe Coevoet", - "email": "stof@notk.org" + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" } ], - "description": "Composer script handling your ignored parameter file", - "homepage": "https://github.com/Incenteev/ParameterHandler", - "keywords": [ - "parameters management" - ], - "time": "2018-02-13T18:05:56+00:00" + "time": "2024-06-17T08:50:25+00:00" }, { - "name": "jms/metadata", - "version": "1.7.0", + "name": "laminas/laminas-zendframework-bridge", + "version": "1.8.0", "source": { "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8" + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/e5854ab1aa643623dc64adde718a8eec32b957a8", - "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/eb0d96c708b92177a92bc2239543d3ed523452c6", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { - "doctrine/cache": "~1.0", - "symfony/cache": "~3.1" + "phpunit/phpunit": "^10.4", + "psalm/plugin-phpunit": "^0.18.0", + "squizlabs/php_codesniffer": "^3.7.1", + "vimeo/psalm": "^5.16.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" } }, "autoload": { - "psr-0": { - "Metadata\\": "src/" + "files": [ + "src/autoload.php" + ], + "psr-4": { + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, + "description": "Alias legacy ZF class names to Laminas Project equivalents.", + "keywords": [ + "ZendFramework", + "autoloading", + "laminas", + "zf" + ], + "support": { + "forum": "https://discourse.laminas.dev/", + "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", + "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", + "source": "https://github.com/laminas/laminas-zendframework-bridge" + }, + "funding": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" } ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "time": "2018-10-26T12:40:10+00:00" + "abandoned": true, + "time": "2023-11-24T13:56:19+00:00" }, { - "name": "jms/parser-lib", - "version": "1.0.0", + "name": "monolog/monolog", + "version": "3.7.0", "source": { "type": "git", - "url": "https://github.com/schmittjoh/parser-lib.git", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", "shasum": "" }, "require": { - "phpoption/phpoption": ">=0.9,<2.0-dev" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.5.17", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "psr-0": { - "JMS\\": "src/" + "psr-4": { + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.7.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } ], - "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" + "time": "2024-06-28T09:40:51+00:00" }, { - "name": "jms/serializer", - "version": "1.13.0", + "name": "ocramius/proxy-manager", + "version": "2.14.1", "source": { "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c" + "url": "https://github.com/Ocramius/ProxyManager.git", + "reference": "3990d60ef79001badbab4927a6a811682274a0d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/00863e1d55b411cc33ad3e1de09a4c8d3aae793c", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/3990d60ef79001badbab4927a6a811682274a0d1", + "reference": "3990d60ef79001badbab4927a6a811682274a0d1", "shasum": "" }, "require": { - "doctrine/annotations": "^1.0", - "doctrine/instantiator": "^1.0.3", - "jms/metadata": "^1.3", - "jms/parser-lib": "1.*", - "php": "^5.5|^7.0", - "phpcollection/phpcollection": "~0.1", - "phpoption/phpoption": "^1.1" + "composer-runtime-api": "^2.1.0", + "laminas/laminas-code": "^4.4.2", + "php": "~8.0.0", + "webimpress/safe-writer": "^2.2.0" }, "conflict": { - "twig/twig": "<1.12" + "thecodingmachine/safe": "<1.3.3" }, "require-dev": { - "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "phpunit/phpunit": "^4.8|^5.0", - "propel/propel1": "~1.7", - "psr/container": "^1.0", - "symfony/dependency-injection": "^2.7|^3.3|^4.0", - "symfony/expression-language": "^2.6|^3.0", - "symfony/filesystem": "^2.1", - "symfony/form": "~2.1|^3.0", - "symfony/translation": "^2.1|^3.0", - "symfony/validator": "^2.2|^3.0", - "symfony/yaml": "^2.1|^3.0", - "twig/twig": "~1.12|~2.0" + "codelicia/xulieta": "^0.1.6", + "doctrine/coding-standard": "^9.0.0", + "ext-phar": "*", + "phpbench/phpbench": "^1.0.3", + "phpunit/phpunit": "^9.5.6", + "roave/infection-static-analysis-plugin": "^1.8", + "squizlabs/php_codesniffer": "^3.6.0", + "vimeo/psalm": "^4.8.1" }, "suggest": { - "doctrine/cache": "Required if you like to use cache functionality.", - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/yaml": "Required if you'd like to serialize data to YAML format." + "laminas/laminas-json": "To have the JsonRpc adapter (Remote Object feature)", + "laminas/laminas-soap": "To have the Soap adapter (Remote Object feature)", + "laminas/laminas-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)", + "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects" }, "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.13-dev" - } - }, "autoload": { - "psr-0": { - "JMS\\Serializer": "src/" + "psr-4": { + "ProxyManager\\": "src/ProxyManager" } }, "notification-url": "https://packagist.org/downloads/", @@ -1364,124 +1534,131 @@ ], "authors": [ { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" } ], - "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", - "homepage": "http://jmsyst.com/libs/serializer", + "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", + "homepage": "https://github.com/Ocramius/ProxyManager", "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" + "aop", + "lazy loading", + "proxy", + "proxy pattern", + "service proxies" + ], + "support": { + "issues": "https://github.com/Ocramius/ProxyManager/issues", + "source": "https://github.com/Ocramius/ProxyManager/tree/2.14.1" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", + "type": "tidelift" + } ], - "time": "2018-07-25T13:58:54+00:00" + "time": "2022-03-05T18:43:14+00:00" }, { - "name": "jms/serializer-bundle", - "version": "2.4.4", + "name": "promphp/prometheus_client_php", + "version": "v2.11.0", "source": { "type": "git", - "url": "https://github.com/schmittjoh/JMSSerializerBundle.git", - "reference": "92ee808c64c1c180775a0e57d00e3be0674668fb" + "url": "https://github.com/PromPHP/prometheus_client_php.git", + "reference": "35d5a68628ea18209938bc1b8796646015ab93cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/JMSSerializerBundle/zipball/92ee808c64c1c180775a0e57d00e3be0674668fb", - "reference": "92ee808c64c1c180775a0e57d00e3be0674668fb", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/35d5a68628ea18209938bc1b8796646015ab93cf", + "reference": "35d5a68628ea18209938bc1b8796646015ab93cf", "shasum": "" }, "require": { - "jms/serializer": "^1.10", - "php": "^5.4|^7.0", - "phpoption/phpoption": "^1.1.0", - "symfony/framework-bundle": "~2.3|~3.0|~4.0" + "ext-json": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "endclothing/prometheus_client_php": "*", + "jimdo/prometheus_client_php": "*", + "lkaemmerling/prometheus_client_php": "*" }, "require-dev": { - "doctrine/orm": "*", - "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0", - "symfony/expression-language": "~2.6|~3.0|~4.0", - "symfony/finder": "^2.3|^3.0|^4.0", - "symfony/form": "*", - "symfony/stopwatch": "*", - "symfony/twig-bundle": "*", - "symfony/validator": "*", - "symfony/yaml": "*" + "guzzlehttp/guzzle": "^6.3|^7.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5.4", + "phpstan/phpstan-phpunit": "^1.1.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpunit/phpunit": "^9.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/polyfill-apcu": "^1.6" }, "suggest": { - "jms/di-extra-bundle": "Required to get lazy loading (de)serialization visitors, ~1.3", - "symfony/finder": "Required for cache warmup, supported versions ^2.3|^3.0|^4.0" + "ext-apc": "Required if using APCu.", + "ext-pdo": "Required if using PDO.", + "ext-redis": "Required if using Redis.", + "promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.", + "symfony/polyfill-apcu": "Required if you use APCu on PHP8.0+" }, - "type": "symfony-bundle", + "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "JMS\\SerializerBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Prometheus\\": "src/Prometheus/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Lukas KΓ€mmerling", + "email": "kontakt@lukas-kaemmerling.de" } ], - "description": "Allows you to easily serialize, and deserialize data of any complexity", - "homepage": "http://jmsyst.com/bundles/JMSSerializerBundle", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" - ], - "time": "2019-03-30T10:26:09+00:00" + "description": "Prometheus instrumentation library for PHP applications.", + "support": { + "issues": "https://github.com/PromPHP/prometheus_client_php/issues", + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.11.0" + }, + "time": "2024-08-05T07:58:08+00:00" }, { - "name": "lambdish/phunctional", - "version": "v1.0.6", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/Lambdish/phunctional.git", - "reference": "46fe90f9b2f7eb2c02d077a53ba05fc75396e7ad" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Lambdish/phunctional/zipball/46fe90f9b2f7eb2c02d077a53ba05fc75396e7ad", - "reference": "46fe90f9b2f7eb2c02d077a53ba05fc75396e7ad", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.6.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.0" + "php": ">=8.0.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "files": [ - "src/_bootstrap.php" - ] + "psr-4": { + "Psr\\Cache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1489,123 +1666,85 @@ ], "authors": [ { - "name": "Jorge Ávila", - "email": "avilacardenosa@gmail.com" - }, - { - "name": "Rafa GΓ³mez", - "email": "rgomezcasas@gmail.com" - }, - { - "name": "Eloi Poch", - "email": "eloi.poch@gmail.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Ξ» PHP functional library", + "description": "Common interface for caching libraries", "keywords": [ - "functional", - "generator", - "lambda", - "library", - "php" + "cache", + "psr", + "psr-6" ], - "time": "2018-10-21T08:21:53+00:00" + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" }, { - "name": "maknz/slack", - "version": "1.7.0", + "name": "psr/clock", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/maknz/slack.git", - "reference": "7f21fefc70c76b304adc1b3a780c8740dfcfb595" + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maknz/slack/zipball/7f21fefc70c76b304adc1b3a780c8740dfcfb595", - "reference": "7f21fefc70c76b304adc1b3a780c8740dfcfb595", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", "shasum": "" }, "require": { - "ext-mbstring": "*", - "guzzlehttp/guzzle": "~6.0|~5.0|~4.0", - "php": ">=5.4.0" - }, - "require-dev": { - "mockery/mockery": "0.9.*", - "phpunit/phpunit": "4.2.*" - }, - "suggest": { - "illuminate/support": "Required for Laravel support" + "php": "^7.0 || ^8.0" }, "type": "library", "autoload": { "psr-4": { - "Maknz\\Slack\\": "src/" + "Psr\\Clock\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], "authors": [ { - "name": "maknz", - "email": "github@mak.geek.nz" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "A simple PHP package for sending messages to Slack, with a focus on ease of use and elegant syntax. Includes Laravel support out of the box.", + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", "keywords": [ - "laravel", - "slack" + "clock", + "now", + "psr", + "psr-20", + "time" ], - "time": "2015-06-03T03:35:16+00:00" + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" }, { - "name": "monolog/monolog", - "version": "1.24.0", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "php": ">=7.4.0" }, "type": "library", "extra": { @@ -1615,7 +1754,7 @@ }, "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1624,55 +1763,51 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "log", - "logging", - "psr-3" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "time": "2018-11-05T09:00:11+00:00" + "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": "ocramius/package-versions", - "version": "1.4.0", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/Ocramius/PackageVersions.git", - "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb", - "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0.0", - "php": "^7.1.0" - }, - "require-dev": { - "composer/composer": "^1.6.3", - "doctrine/coding-standard": "^5.0.1", - "ext-zip": "*", - "infection/infection": "^0.7.1", - "phpunit/phpunit": "^7.0.0" + "php": ">=7.2.0" }, - "type": "composer-plugin", + "type": "library", "extra": { - "class": "PackageVersions\\Installer", "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "PackageVersions\\": "src/PackageVersions" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1681,59 +1816,48 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "time": "2019-02-21T12:16:21+00:00" + "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": "ocramius/proxy-manager", - "version": "2.2.2", + "name": "psr/log", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/Ocramius/ProxyManager.git", - "reference": "14b137b06b0f911944132df9d51e445a35920ab1" + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/14b137b06b0f911944132df9d51e445a35920ab1", - "reference": "14b137b06b0f911944132df9d51e445a35920ab1", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "ocramius/package-versions": "^1.1.3", - "php": "^7.2.0", - "zendframework/zend-code": "^3.3.0" - }, - "require-dev": { - "couscous/couscous": "^1.6.1", - "ext-phar": "*", - "humbug/humbug": "1.0.0-RC.0@RC", - "nikic/php-parser": "^3.1.1", - "padraic/phpunit-accelerator": "dev-master@DEV", - "phpbench/phpbench": "^0.12.2", - "phpstan/phpstan": "dev-master#856eb10a81c1d27c701a83f167dc870fd8f4236a as 0.9.999", - "phpstan/phpstan-phpunit": "dev-master#5629c0a1f4a9c417cb1077cf6693ad9753895761", - "phpunit/phpunit": "^6.4.3", - "squizlabs/php_codesniffer": "^2.9.1" - }, - "suggest": { - "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", - "zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)", - "zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)", - "zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { - "psr-0": { - "ProxyManager\\": "src" + "psr-4": { + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1742,191 +1866,230 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.io/" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", - "homepage": "https://github.com/Ocramius/ProxyManager", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "aop", - "lazy loading", - "proxy", - "proxy pattern", - "service proxies" + "log", + "psr", + "psr-3" ], - "time": "2018-09-27T13:45:01+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.99", + "name": "ramsey/collection", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", "shasum": "" }, "require": { - "php": "^7" + "php": "^8.1" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "time": "2018-07-02T15:55:56+00:00" - }, - { - "name": "phpcollection/phpcollection", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "shasum": "" - }, - "require": { - "phpoption/phpoption": "1.*" + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "0.4-dev" + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" } }, "autoload": { - "psr-0": { - "PhpCollection": "src/" + "psr-4": { + "Ramsey\\Collection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "MIT" ], "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" } ], - "description": "General-Purpose Collection Library for PHP", + "description": "A PHP library for representing and manipulating collections.", "keywords": [ + "array", "collection", - "list", + "hash", "map", - "sequence", + "queue", "set" ], - "time": "2015-05-17T12:39:23+00:00" + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.5.0", + "name": "ramsey/uuid", + "version": "4.7.6", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", "shasum": "" }, "require": { - "php": ">=5.3.0" + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" }, "require-dev": { - "phpunit/phpunit": "4.7.*" + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.3-dev" + "captainhook": { + "force-install": true } }, "autoload": { - "psr-0": { - "PhpOption\\": "src/" + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "MIT" ], - "authors": [ + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" } ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "time": "2015-07-25T16:39:46+00:00" + "time": "2024-04-27T21:32:50+00:00" }, { - "name": "psr/cache", - "version": "1.0.1", + "name": "react/promise", + "version": "v2.11.0", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "url": "https://github.com/reactphp/promise.git", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, + "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "Psr\\Cache\\": "src/" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1935,45 +2098,100 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian LΓΌck", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interface for caching libraries", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "cache", - "psr", - "psr-6" + "promise", + "promises" ], - "time": "2016-08-06T20:24:11+00:00" + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.11.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-16T16:16:50+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "symfony/cache", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/symfony/cache.git", + "reference": "8ac37acee794372f9732fe8a61a8221f6762148e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/symfony/cache/zipball/8ac37acee794372f9732fe8a61a8221f6762148e", + "reference": "8ac37acee794372f9732fe8a61a8221f6762148e", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" }, + "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": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Container\\": "src/" - } + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1981,47 +2199,70 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "caching", + "psr6" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "source": "https://github.com/symfony/cache/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-17T06:10:24+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "symfony/cache-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.1", + "psr/cache": "^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Symfony\\Contracts\\Cache\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2030,49 +2271,76 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.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": "2016-08-06T14:39:51+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "psr/log", - "version": "1.1.0", + "name": "symfony/clock", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "url": "https://github.com/symfony/clock.git", + "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", + "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "provide": { + "psr/clock-implementation": "1.0" }, + "type": "library", "autoload": { + "files": [ + "Resources/now.php" + ], "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2080,46 +2348,79 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", "keywords": [ - "log", - "psr", - "psr-3" + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2018-11-20T15:27:04+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "psr/simple-cache", - "version": "1.0.1", + "name": "symfony/config", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "url": "https://github.com/symfony/config.git", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/symfony/config/zipball/2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", + "symfony/polyfill-ctype": "~1.8" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\SimpleCache\\": "src/" - } + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2127,45 +2428,85 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" + "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/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2017-10-23T01:57:42+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "2.0.5", + "name": "symfony/console", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" + "url": "https://github.com/symfony/console.git", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { - "files": [ - "src/getallheaders.php" + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2174,66 +2515,85 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "A polyfill for getallheaders.", - "time": "2016-02-11T07:05:27+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { - "name": "ramsey/uuid", - "version": "3.8.0", + "name": "symfony/dependency-injection", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "8126f0be4ff984e4db0140e60917900a53facb49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8126f0be4ff984e4db0140e60917900a53facb49", + "reference": "8126f0be4ff984e4db0140e60917900a53facb49", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.5", + "symfony/var-exporter": "^6.4|^7.0" }, - "replace": { - "rhumsaa/uuid": "self.version" + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" }, - "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" }, - "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "require-dev": { + "symfony/config": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" - } + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2241,58 +2601,65 @@ ], "authors": [ { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" + "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/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" + "url": "https://github.com/fabpot", + "type": "github" }, { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "time": "2018-07-19T23:38:55+00:00" + "time": "2024-07-26T07:35:39+00:00" }, { - "name": "swiftmailer/swiftmailer", - "version": "v5.4.12", + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "181b89f18a90f8925ef805f950d47a7190e9b950" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/181b89f18a90f8925ef805f950d47a7190e9b950", - "reference": "181b89f18a90f8925ef805f950d47a7190e9b950", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "mockery/mockery": "~0.9.1", - "symfony/phpunit-bridge": "~3.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.4-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "files": [ - "lib/swift_required.php" + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2301,72 +2668,64 @@ ], "authors": [ { - "name": "Chris Corbyn" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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": "2018-07-31T09:26:32+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "symfony/cache", - "version": "v4.2.5", + "name": "symfony/dotenv", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "3172c1e81fab8a21b35732ad8545e09877bb73d9" + "url": "https://github.com/symfony/dotenv.git", + "reference": "a26be30fd61678dab694a18a85084cea7673bbf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/3172c1e81fab8a21b35732ad8545e09877bb73d9", - "reference": "3172c1e81fab8a21b35732ad8545e09877bb73d9", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/a26be30fd61678dab694a18a85084cea7673bbf3", + "reference": "a26be30fd61678dab694a18a85084cea7673bbf3", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/cache": "~1.0", - "psr/log": "~1.0", - "psr/simple-cache": "^1.0", - "symfony/contracts": "^1.0", - "symfony/var-exporter": "^4.2" + "php": ">=8.2" }, "conflict": { - "doctrine/dbal": "<2.5", - "symfony/dependency-injection": "<3.4", - "symfony/var-dumper": "<3.4" - }, - "provide": { - "psr/cache-implementation": "1.0", - "psr/simple-cache-implementation": "1.0", - "symfony/cache-contracts-implementation": "1.0" + "symfony/console": "<6.4", + "symfony/process": "<6.4" }, "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/cache": "~1.6", - "doctrine/dbal": "~2.5", - "predis/predis": "~1.1", - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.1", - "symfony/var-dumper": "^4.1.1" + "symfony/console": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Cache\\": "" + "Symfony\\Component\\Dotenv\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2378,62 +2737,75 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "description": "Registers environment variables from a .env file", "homepage": "https://symfony.com", "keywords": [ - "caching", - "psr6" + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2019-04-01T07:32:59+00:00" + "time": "2024-07-09T19:36:07+00:00" }, { - "name": "symfony/config", - "version": "v4.2.5", + "name": "symfony/error-handler", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "0e745ead307d5dcd4e163e94a47ec04b1428943f" + "url": "https://github.com/symfony/error-handler.git", + "reference": "432bb369952795c61ca1def65e078c4a80dad13c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/0e745ead307d5dcd4e163e94a47ec04b1428943f", - "reference": "0e745ead307d5dcd4e163e94a47ec04b1428943f", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c", + "reference": "432bb369952795c61ca1def65e078c4a80dad13c", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" }, "conflict": { - "symfony/finder": "<3.4" + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Config\\": "" + "Symfony\\Component\\ErrorHandler\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2453,59 +2825,67 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Config Component", + "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", - "time": "2019-04-01T14:03:25+00:00" + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T13:02:51+00:00" }, { - "name": "symfony/console", - "version": "v4.2.5", + "name": "symfony/event-dispatcher", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "24206aff3efe6962593297e57ef697ebb220e384" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", - "reference": "24206aff3efe6962593297e57ef697ebb220e384", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" }, "provide": { - "psr/log-implementation": "1.0" + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" + "Symfony\\Component\\EventDispatcher\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2517,58 +2897,140 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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": "Symfony Console Component", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", - "time": "2019-04-01T07:32:59+00:00" + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.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-04-18T09:32:20+00:00" }, { - "name": "symfony/contracts", - "version": "v1.0.2", + "name": "symfony/filesystem", + "version": "v7.1.2", "source": { "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" + "url": "https://github.com/symfony/filesystem.git", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" + "symfony/process": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Contracts\\": "" + "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ - "**/Tests/" + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2577,59 +3039,59 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "A set of abstractions extracted out of the Symfony components", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2018-12-05T08:06:11+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { - "name": "symfony/debug", - "version": "v4.2.5", + "name": "symfony/finder", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "43ce8ab34c734dcc8a4af576cb86711daab964c5" + "url": "https://github.com/symfony/finder.git", + "reference": "717c6329886f32dc65e27461f80f2a465412fdca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/43ce8ab34c734dcc8a4af576cb86711daab964c5", - "reference": "43ce8ab34c734dcc8a4af576cb86711daab964c5", + "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", + "reference": "717c6329886f32dc65e27461f80f2a465412fdca", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": "<3.4" + "php": ">=8.2" }, "require-dev": { - "symfony/http-kernel": "~3.4|~4.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Debug\\": "" + "Symfony\\Component\\Finder\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2649,60 +3111,134 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Debug Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "time": "2019-03-10T17:09:50+00:00" + "support": { + "source": "https://github.com/symfony/finder/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-24T07:08:44+00:00" }, { - "name": "symfony/dependency-injection", - "version": "v4.2.5", + "name": "symfony/framework-bundle", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "1806e43ff6bff57398d33b326cd753a12d9f434f" + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "a32ec544bd501eb4619eb977860ad3076ee55061" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1806e43ff6bff57398d33b326cd753a12d9f434f", - "reference": "1806e43ff6bff57398d33b326cd753a12d9f434f", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a32ec544bd501eb4619eb977860ad3076ee55061", + "reference": "a32ec544bd501eb4619eb977860ad3076ee55061", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/container": "^1.0", - "symfony/contracts": "^1.0" + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^7.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/filesystem": "^7.1", + "symfony/finder": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^6.4|^7.0" }, "conflict": { - "symfony/config": "<4.2", - "symfony/finder": "<3.4", - "symfony/proxy-manager-bridge": "<3.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "psr/container-implementation": "1.0", - "symfony/service-contracts-implementation": "1.0" + "doctrine/persistence": "<1.3", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/asset": "<6.4", + "symfony/asset-mapper": "<6.4", + "symfony/clock": "<6.4", + "symfony/console": "<6.4", + "symfony/dom-crawler": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/lock": "<6.4", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/property-access": "<6.4", + "symfony/property-info": "<6.4", + "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", + "symfony/security-core": "<6.4", + "symfony/security-csrf": "<6.4", + "symfony/serializer": "<6.4", + "symfony/stopwatch": "<6.4", + "symfony/translation": "<6.4", + "symfony/twig-bridge": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4", + "symfony/web-profiler-bundle": "<6.4", + "symfony/workflow": "<6.4" }, "require-dev": { - "symfony/config": "~4.2", - "symfony/expression-language": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "doctrine/persistence": "^1.3|^2|^3", + "dragonmantank/cron-expression": "^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "seld/jsonlint": "^1.10", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/dotenv": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/mailer": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/notifier": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/scheduler": "^6.4.4|^7.0.4", + "symfony/security-bundle": "^6.4|^7.0", + "symfony/semaphore": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/type-info": "^7.1", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, + "type": "symfony-bundle", "autoload": { "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" + "Symfony\\Bundle\\FrameworkBundle\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2722,39 +3258,64 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony DependencyInjection Component", + "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", - "time": "2019-03-30T15:58:42+00:00" + "support": { + "source": "https://github.com/symfony/framework-bundle/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T13:24:34+00:00" }, { - "name": "symfony/dotenv", - "version": "v4.2.5", + "name": "symfony/http-foundation", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "b541d63b83532be55a020db8ed2e50598385a583" + "url": "https://github.com/symfony/http-foundation.git", + "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/b541d63b83532be55a020db8ed2e50598385a583", - "reference": "b541d63b83532be55a020db8ed2e50598385a583", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f602d5c17d1fa02f8019ace2687d9d136b7f4a1a", + "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4" }, "require-dev": { - "symfony/process": "~3.4|~4.0" + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Dotenv\\": "" + "Symfony\\Component\\HttpFoundation\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2774,56 +3335,101 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Registers environment variables from a .env file", + "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", - "keywords": [ - "dotenv", - "env", - "environment" + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2019-04-01T07:32:59+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v4.2.5", + "name": "symfony/http-kernel", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544" + "url": "https://github.com/symfony/http-kernel.git", + "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", - "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/db9702f3a04cc471ec8c70e881825db26ac5f186", + "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/dependency-injection": "<3.4" + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.0.4" }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" + "Symfony\\Component\\HttpKernel\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2843,37 +3449,73 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", - "time": "2019-03-30T15:58:42+00:00" + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T14:58:15+00:00" }, { - "name": "symfony/filesystem", - "version": "v4.2.5", + "name": "symfony/messenger", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "e16b9e471703b2c60b95f14d31c1239f68f11601" + "url": "https://github.com/symfony/messenger.git", + "reference": "604e182a7758ceea35921a8ad5dd492a6e13bae4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e16b9e471703b2c60b95f14d31c1239f68f11601", - "reference": "e16b9e471703b2c60b95f14d31c1239f68f11601", + "url": "https://api.github.com/repos/symfony/messenger/zipball/604e182a7758ceea35921a8ad5dd492a6e13bae4", + "reference": "604e182a7758ceea35921a8ad5dd492a6e13bae4", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/clock": "^6.4|^7.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "conflict": { + "symfony/console": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/framework-bundle": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/serializer": "<6.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" + "Symfony\\Component\\Messenger\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2885,48 +3527,72 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Samuel Roze", + "email": "samuel.roze@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", - "time": "2019-02-07T11:40:08+00:00" + "support": { + "source": "https://github.com/symfony/messenger/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-09T19:36:07+00:00" }, { - "name": "symfony/finder", - "version": "v4.2.5", + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/267b7002c1b70ea80db0833c3afe05f0fbde580a", - "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2934,118 +3600,75 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", - "time": "2019-02-23T15:42:05+00:00" + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.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-05-31T15:07:36+00:00" }, { - "name": "symfony/framework-bundle", - "version": "v4.2.5", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/framework-bundle.git", - "reference": "2cd66337a7effcdaaa23b4ac4541a5cffbbb7a61" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/2cd66337a7effcdaaa23b4ac4541a5cffbbb7a61", - "reference": "2cd66337a7effcdaaa23b4ac4541a5cffbbb7a61", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { - "ext-xml": "*", - "php": "^7.1.3", - "symfony/cache": "~4.2", - "symfony/config": "~4.2", - "symfony/contracts": "^1.0.2", - "symfony/dependency-injection": "^4.2.5", - "symfony/event-dispatcher": "^4.1", - "symfony/filesystem": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/http-foundation": "^4.2.5", - "symfony/http-kernel": "^4.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/routing": "^4.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.1", - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/asset": "<3.4", - "symfony/console": "<3.4", - "symfony/dotenv": "<4.2", - "symfony/form": "<4.2", - "symfony/messenger": "<4.2", - "symfony/property-info": "<3.4", - "symfony/serializer": "<4.2", - "symfony/stopwatch": "<3.4", - "symfony/translation": "<4.2", - "symfony/twig-bridge": "<4.1.1", - "symfony/validator": "<4.1", - "symfony/workflow": "<4.1" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "fig/link-util": "^1.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0", - "symfony/asset": "~3.4|~4.0", - "symfony/browser-kit": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/form": "^4.2.3", - "symfony/lock": "~3.4|~4.0", - "symfony/messenger": "^4.2", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "~3.4|~4.0", - "symfony/property-info": "~3.4|~4.0", - "symfony/security": "~3.4|~4.0", - "symfony/security-core": "~3.4|~4.0", - "symfony/security-csrf": "~3.4|~4.0", - "symfony/serializer": "^4.2", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~4.2", - "symfony/validator": "^4.1", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/web-link": "~3.4|~4.0", - "symfony/workflow": "^4.1", - "symfony/yaml": "~3.4|~4.0", - "twig/twig": "~1.34|~2.4" + "php": ">=7.1" }, "suggest": { - "ext-apcu": "For best performance of the system caches", - "symfony/console": "For using the console commands", - "symfony/form": "For using forms", - "symfony/property-info": "For using the property_info service", - "symfony/serializer": "For using the serializer service", - "symfony/validator": "For using validation", - "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", - "symfony/yaml": "For using the debug:config and lint:yaml commands" + "ext-intl": "For best performance" }, - "type": "symfony-bundle", + "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Bundle\\FrameworkBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3053,52 +3676,79 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony FrameworkBundle", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", - "time": "2019-04-01T07:32:59+00:00" + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.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-05-31T15:07:36+00:00" }, { - "name": "symfony/http-foundation", - "version": "v4.2.5", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "5b7ab6beaa5b053b8d3c9b13367ada9b292e12e1" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5b7ab6beaa5b053b8d3c9b13367ada9b292e12e1", - "reference": "5b7ab6beaa5b053b8d3c9b13367ada9b292e12e1", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.1" + "php": ">=7.1" }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3107,88 +3757,80 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpFoundation Component", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", - "time": "2019-03-30T15:58:42+00:00" + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.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-05-31T15:07:36+00:00" }, { - "name": "symfony/http-kernel", - "version": "v4.2.5", + "name": "symfony/polyfill-mbstring", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "e8b940bbeebf0f96789b5d17d9d77f8b2613960b" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e8b940bbeebf0f96789b5d17d9d77f8b2613960b", - "reference": "e8b940bbeebf0f96789b5d17d9d77f8b2613960b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/log": "~1.0", - "symfony/contracts": "^1.0.2", - "symfony/debug": "~3.4|~4.0", - "symfony/event-dispatcher": "~4.1", - "symfony/http-foundation": "^4.1.1", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<4.2", - "symfony/translation": "<4.2", - "symfony/var-dumper": "<4.1.1", - "twig/twig": "<1.34|<2.4,>=2" + "php": ">=7.1" }, "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/cache": "~1.0", - "symfony/browser-kit": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dependency-injection": "^4.2", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~4.2", - "symfony/var-dumper": "^4.1.1" + "ext-mbstring": "*" }, "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/var-dumper": "" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3196,130 +3838,148 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpKernel Component", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", - "time": "2019-04-02T19:03:51+00:00" + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.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-06-19T12:30:46+00:00" }, { - "name": "symfony/messenger", - "version": "v4.2.5", + "name": "symfony/polyfill-php72", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/messenger.git", - "reference": "a84da6594ea9935a06546fb0e3ff6607b70b2932" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "10112722600777e02d2745716b70c5db4ca70442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/a84da6594ea9935a06546fb0e3ff6607b70b2932", - "reference": "a84da6594ea9935a06546fb0e3ff6607b70b2932", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", + "reference": "10112722600777e02d2745716b70c5db4ca70442", "shasum": "" }, "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4.19|^4.1.8", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/property-access": "~3.4|~4.0", - "symfony/serializer": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0" - }, - "suggest": { - "enqueue/messenger-adapter": "For using the php-enqueue library as a transport." + "php": ">=7.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Messenger\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Php72\\": "" + } }, "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 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Samuel Roze", - "email": "samuel.roze@gmail.com" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony Messenger Component", - "homepage": "https://symfony.com", - "time": "2019-03-12T17:23:22+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { - "name": "symfony/monolog-bridge", - "version": "v4.2.5", + "name": "symfony/polyfill-php80", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "472c74b350542e51373dcb159c0dcc234dc74e38" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/472c74b350542e51373dcb159c0dcc234dc74e38", - "reference": "472c74b350542e51373dcb159c0dcc234dc74e38", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { - "monolog/monolog": "~1.19", - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/http-kernel": "~3.4|~4.0" - }, - "conflict": { - "symfony/console": "<3.4", - "symfony/http-foundation": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/security-core": "~3.4|~4.0", - "symfony/var-dumper": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", - "symfony/event-dispatcher": "Needed when using log messages in console commands.", - "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", - "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + "php": ">=7.1" }, - "type": "symfony-bridge", + "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.2-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Bridge\\Monolog\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3328,57 +3988,78 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Monolog Bridge", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", - "time": "2019-02-28T17:49:31+00:00" + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.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-05-31T15:07:36+00:00" }, { - "name": "symfony/monolog-bundle", - "version": "v3.3.1", + "name": "symfony/polyfill-php83", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "572e143afc03419a75ab002c80a2fd99299195ff" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/572e143afc03419a75ab002c80a2fd99299195ff", - "reference": "572e143afc03419a75ab002c80a2fd99299195ff", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", "shasum": "" }, "require": { - "monolog/monolog": "~1.22", - "php": ">=5.6", - "symfony/config": "~2.7|~3.3|~4.0", - "symfony/dependency-injection": "~2.7|~3.4.10|^4.0.10", - "symfony/http-kernel": "~2.7|~3.3|~4.0", - "symfony/monolog-bridge": "~2.7|~3.3|~4.0" - }, - "require-dev": { - "symfony/console": "~2.7|~3.3|~4.0", - "symfony/phpunit-bridge": "^3.3|^4.0", - "symfony/yaml": "~2.7|~3.3|~4.0" + "php": ">=7.1" }, - "type": "symfony-bundle", + "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3387,54 +4068,79 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony MonologBundle", - "homepage": "http://symfony.com", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "log", - "logging" + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.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": "2018-11-04T09:58:13+00:00" + "time": "2024-06-19T12:35:24+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "name": "symfony/routing", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "url": "https://github.com/symfony/routing.git", + "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/symfony/routing/zipball/8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", + "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, - "suggest": { - "ext-ctype": "For best performance" + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Component\\Routing\\": "" }, - "files": [ - "bootstrap.php" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3443,56 +4149,79 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Maps an HTTP request to a set of configuration variables", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2024-07-17T06:10:24+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "name": "symfony/service-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "suggest": { - "ext-mbstring": "For best performance" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Contracts\\Service\\": "" }, - "files": [ - "bootstrap.php" + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3501,51 +4230,82 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "symfony/process", - "version": "v4.2.5", + "name": "symfony/string", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6" + "url": "https://github.com/symfony/string.git", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", - "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", + "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, + "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3557,68 +4317,76 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "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", - "time": "2019-03-10T20:07:02+00:00" + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-22T10:25:37+00:00" }, { - "name": "symfony/routing", - "version": "v4.2.5", + "name": "symfony/translation-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "319f600c1ea0f981f6bdc2f042cfc1690957c0e0" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/319f600c1ea0f981f6bdc2f042cfc1690957c0e0", - "reference": "319f600c1ea0f981f6bdc2f042cfc1690957c0e0", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", "shasum": "" }, "require": { - "php": "^7.1.3" - }, - "conflict": { - "symfony/config": "<4.2", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "psr/log": "~1.0", - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Routing\\": "" + "Symfony\\Contracts\\Translation\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3627,68 +4395,112 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Routing Component", + "description": "Generic abstractions related to translation", "homepage": "https://symfony.com", "keywords": [ - "router", - "routing", - "uri", - "url" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.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": "2019-03-30T15:58:42+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "symfony/security-core", - "version": "v4.2.5", + "name": "symfony/twig-bridge", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/security-core.git", - "reference": "d4d72ea26792370db1079fe9ecec707694482f1e" + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "96e6e12a63db80bcedefc012042d2cb2d1a015f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/d4d72ea26792370db1079fe9ecec707694482f1e", - "reference": "d4d72ea26792370db1079fe9ecec707694482f1e", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/96e6e12a63db80bcedefc012042d2cb2d1a015f8", + "reference": "96e6e12a63db80bcedefc012042d2cb2d1a015f8", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0" - }, - "require-dev": { - "psr/container": "^1.0", - "psr/log": "~1.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/ldap": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0" + "php": ">=8.2", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^3.9" }, - "suggest": { - "psr/container-implementation": "To instantiate the Security class", - "symfony/event-dispatcher": "", - "symfony/expression-language": "For using the expression voter", - "symfony/http-foundation": "", - "symfony/ldap": "For using LDAP integration", - "symfony/validator": "For using the user password constraint" + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<6.4", + "symfony/form": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.4", + "symfony/serializer": "<6.4", + "symfony/translation": "<6.4", + "symfony/workflow": "<6.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "require-dev": { + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/emoji": "^7.1", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" }, + "type": "symfony-bridge", "autoload": { "psr-4": { - "Symfony\\Component\\Security\\Core\\": "" + "Symfony\\Bridge\\Twig\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3708,43 +4520,71 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Security Component - Core Library", + "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", - "time": "2019-03-19T21:07:50+00:00" + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/templating", - "version": "v4.2.5", + "name": "symfony/twig-bundle", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/templating.git", - "reference": "1bb2d2eda3136fff122b8810ac1357440411abeb" + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "d48c2f08c2f315e749f0e18fc4945b7be8afe1e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/templating/zipball/1bb2d2eda3136fff122b8810ac1357440411abeb", - "reference": "1bb2d2eda3136fff122b8810ac1357440411abeb", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/d48c2f08c2f315e749f0e18fc4945b7be8afe1e5", + "reference": "d48c2f08c2f315e749f0e18fc4945b7be8afe1e5", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "require-dev": { - "psr/log": "~1.0" + "composer-runtime-api": ">=2.1", + "php": ">=8.2", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, - "suggest": { - "psr/log-implementation": "For using debug logging in loaders" + "conflict": { + "symfony/framework-bundle": "<6.4", + "symfony/translation": "<6.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, + "type": "symfony-bundle", "autoload": { "psr-4": { - "Symfony\\Component\\Templating\\": "" + "Symfony\\Bundle\\TwigBundle\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3764,82 +4604,87 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Templating Component", + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "support": { + "source": "https://github.com/symfony/twig-bundle/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/twig-bridge", - "version": "v4.2.5", + "name": "symfony/validator", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bridge.git", - "reference": "ae8c3faafec299e8f4e500c0b96cb0e52cfcee90" + "url": "https://github.com/symfony/validator.git", + "reference": "ba711a6cfc008544dad059abb3c1d997f1472237" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/ae8c3faafec299e8f4e500c0b96cb0e52cfcee90", - "reference": "ae8c3faafec299e8f4e500c0b96cb0e52cfcee90", + "url": "https://api.github.com/repos/symfony/validator/zipball/ba711a6cfc008544dad059abb3c1d997f1472237", + "reference": "ba711a6cfc008544dad059abb3c1d997f1472237", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0.2", - "twig/twig": "^1.38.1|^2.7.1" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" }, "conflict": { - "symfony/console": "<3.4", - "symfony/form": "<4.2.4", - "symfony/translation": "<4.2" + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<7.0", + "symfony/expression-language": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/intl": "<6.4", + "symfony/property-info": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/yaml": "<6.4" }, "require-dev": { - "symfony/asset": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/form": "^4.2.4", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~3.4|~4.0", - "symfony/security": "~3.4|~4.0", - "symfony/security-acl": "~2.8|~3.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~4.2", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/web-link": "~3.4|~4.0", - "symfony/workflow": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/asset": "For using the AssetExtension", - "symfony/expression-language": "For using the ExpressionExtension", - "symfony/finder": "", - "symfony/form": "For using the FormExtension", - "symfony/http-kernel": "For using the HttpKernelExtension", - "symfony/routing": "For using the RoutingExtension", - "symfony/security": "For using the SecurityExtension", - "symfony/stopwatch": "For using the StopwatchExtension", - "symfony/templating": "For using the TwigEngine", - "symfony/translation": "For using the TranslationExtension", - "symfony/var-dumper": "For using the DumpExtension", - "symfony/web-link": "For using the WebLinkExtension", - "symfony/yaml": "For using the YamlExtension" - }, - "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "egulias/email-validator": "^2.1.10|^3|^4", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/translation": "^6.4.3|^7.0.3", + "symfony/type-info": "^7.1", + "symfony/yaml": "^6.4|^7.0" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Bridge\\Twig\\": "" + "Symfony\\Component\\Validator\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/bin/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3856,63 +4701,66 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Twig Bridge", + "description": "Provides tools to validate values", "homepage": "https://symfony.com", - "time": "2019-04-01T08:18:15+00:00" + "support": { + "source": "https://github.com/symfony/validator/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T12:41:01+00:00" }, { - "name": "symfony/twig-bundle", - "version": "v4.2.5", + "name": "symfony/var-dumper", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bundle.git", - "reference": "6c6e3be7020563c36a34139bef94fed0735cdf9e" + "url": "https://github.com/symfony/var-dumper.git", + "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/6c6e3be7020563c36a34139bef94fed0735cdf9e", - "reference": "6c6e3be7020563c36a34139bef94fed0735cdf9e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/86af4617cca75a6e28598f49ae0690f3b9d4591f", + "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/config": "~4.2", - "symfony/http-foundation": "~4.1", - "symfony/http-kernel": "~4.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/twig-bridge": "^4.2", - "twig/twig": "~1.34|~2.4" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/dependency-injection": "<4.1", - "symfony/framework-bundle": "<4.1", - "symfony/translation": "<4.2" + "symfony/console": "<6.4" }, "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "symfony/asset": "~3.4|~4.0", - "symfony/dependency-injection": "^4.2.5", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/form": "~3.4|~4.0", - "symfony/framework-bundle": "~4.1", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "^4.2", - "symfony/web-link": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", "autoload": { + "files": [ + "Resources/functions/dump.php" + ], "psr-4": { - "Symfony\\Bundle\\TwigBundle\\": "" + "Symfony\\Component\\VarDumper\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3924,44 +4772,62 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony TwigBundle", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", "homepage": "https://symfony.com", - "time": "2019-03-04T11:47:55+00:00" + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/var-exporter", - "version": "v4.2.5", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "d8bf4424c232b55f4c1816037d3077a89258557e" + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/d8bf4424c232b55f4c1816037d3077a89258557e", - "reference": "d8bf4424c232b55f4c1816037d3077a89258557e", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2" }, "require-dev": { - "symfony/var-dumper": "^4.1.1" + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\VarExporter\\": "" @@ -3984,7 +4850,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", "homepage": "https://symfony.com", "keywords": [ "clone", @@ -3992,43 +4858,57 @@ "export", "hydrate", "instantiate", + "lazy-loading", + "proxy", "serialize" ], - "time": "2019-01-16T20:35:37+00:00" + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T08:00:31+00:00" }, { "name": "symfony/yaml", - "version": "v4.2.5", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6712daf03ee25b53abb14e7e8e0ede1a770efdb1" + "reference": "fa34c77015aa6720469db7003567b9f772492bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6712daf03ee25b53abb14e7e8e0ede1a770efdb1", - "reference": "6712daf03ee25b53abb14e7e8e0ede1a770efdb1", + "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<3.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^6.4|^7.0" }, + "bin": [ + "Resources/bin/yaml-lint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" @@ -4051,44 +4931,60 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", - "time": "2019-03-30T15:58:42+00:00" + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { "name": "twig/twig", - "version": "v2.7.4", + "version": "v3.10.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ed9c49220e09bfaeb1ba4d48077c08a7b09908dd" + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ed9c49220e09bfaeb1ba4d48077c08a7b09908dd", - "reference": "ed9c49220e09bfaeb1ba4d48077c08a7b09908dd", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { - "psr/container": "^1.0", - "symfony/debug": "^2.7", - "symfony/phpunit-bridge": "^3.4.19|^4.1.8" + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, "autoload": { - "psr-0": { - "Twig_": "lib/" - }, + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -4104,15 +5000,14 @@ "homepage": "http://fabien.potencier.org", "role": "Lead Developer" }, + { + "name": "Twig Team", + "role": "Contributors" + }, { "name": "Armin Ronacher", "email": "armin.ronacher@active-4.com", "role": "Project Founder" - }, - { - "name": "Twig Team", - "homepage": "https://twig.symfony.com/contributors", - "role": "Contributors" } ], "description": "Twig, the flexible, fast, and secure template language for PHP", @@ -4120,242 +5015,279 @@ "keywords": [ "templating" ], - "time": "2019-03-23T14:28:58+00:00" - }, - { - "name": "willdurand/jsonp-callback-validator", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/willdurand/JsonpCallbackValidator.git", - "reference": "1a7d388bb521959e612ef50c5c7b1691b097e909" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/willdurand/JsonpCallbackValidator/zipball/1a7d388bb521959e612ef50c5c7b1691b097e909", - "reference": "1a7d388bb521959e612ef50c5c7b1691b097e909", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~3.7" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonpCallbackValidator": "src/" - } + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.10.3" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, { - "name": "William Durand", - "email": "william.durand1@gmail.com", - "homepage": "http://www.willdurand.fr" + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" } ], - "description": "JSONP callback validator.", - "time": "2014-01-20T22:35:06+00:00" + "time": "2024-05-16T10:04:27+00:00" }, { - "name": "willdurand/negotiation", - "version": "v2.3.1", + "name": "webimpress/safe-writer", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/willdurand/Negotiation.git", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9" + "url": "https://github.com/webimpress/safe-writer.git", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9", + "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpunit/phpunit": "^9.5.4", + "vimeo/psalm": "^4.7", + "webimpress/coding-standard": "^1.2.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.2.x-dev", + "dev-develop": "2.3.x-dev", + "dev-release-1.0": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Negotiation\\": "src/Negotiation" + "Webimpress\\SafeWriter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-2-Clause" + ], + "description": "Tool to write files safely, to avoid race conditions", + "keywords": [ + "concurrent write", + "file writer", + "race condition", + "safe writer", + "webimpress" ], - "authors": [ + "support": { + "issues": "https://github.com/webimpress/safe-writer/issues", + "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + }, + "funding": [ { - "name": "William Durand", - "email": "will+git@drnd.me" + "url": "https://github.com/michalbundyra", + "type": "github" } ], - "description": "Content Negotiation tools for PHP provided as a standalone library.", - "homepage": "http://williamdurand.fr/Negotiation/", - "keywords": [ - "accept", - "content", - "format", - "header", - "negotiation" - ], - "time": "2017-05-14T17:21:12+00:00" - }, + "time": "2021-04-19T16:34:45+00:00" + } + ], + "packages-dev": [ { - "name": "zendframework/zend-code", - "version": "3.3.1", + "name": "amphp/amp", + "version": "v2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", - "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" + "url": "https://github.com/amphp/amp.git", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", - "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", "shasum": "" }, "require": { - "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "php": ">=7.1" }, "require-dev": { - "doctrine/annotations": "~1.0", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3.x-dev", - "dev-develop": "3.4.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], "psr-4": { - "Zend\\Code\\": "src/" + "Amp\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } ], - "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", "keywords": [ - "code", - "zf2" + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" ], - "time": "2018-08-13T20:36:59+00:00" + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T18:52:26+00:00" }, { - "name": "zendframework/zend-eventmanager", - "version": "3.2.1", + "name": "amphp/byte-stream", + "version": "v1.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd" + "url": "https://github.com/amphp/byte-stream.git", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "amphp/amp": "^2", + "php": ">=7.1" }, "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0" - }, - "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" - } - }, "autoload": { + "files": [ + "lib/functions.php" + ], "psr-4": { - "Zend\\EventManager\\": "src/" + "Amp\\ByteStream\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://github.com/zendframework/zend-eventmanager", + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", "keywords": [ - "event", - "eventmanager", - "events", - "zf2" + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" ], - "time": "2018-04-25T15:33:34+00:00" - } - ], - "packages-dev": [ + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-13T18:00:56+00:00" + }, { "name": "behat/behat", - "version": "v3.5.0", + "version": "v3.14.0", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "e4bce688be0c2029dc1700e46058d86428c63cab" + "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/e4bce688be0c2029dc1700e46058d86428c63cab", - "reference": "e4bce688be0c2029dc1700e46058d86428c63cab", + "url": "https://api.github.com/repos/Behat/Behat/zipball/2a3832d9cb853a794af3a576f9e524ae460f3340", + "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340", "shasum": "" }, "require": { - "behat/gherkin": "^4.5.1", + "behat/gherkin": "^4.9.0", "behat/transliterator": "^1.2", - "container-interop/container-interop": "^1.2", "ext-mbstring": "*", - "php": ">=5.3.3", - "psr/container": "^1.0", - "symfony/class-loader": "~2.1||~3.0", - "symfony/config": "~2.3||~3.0||~4.0", - "symfony/console": "~2.7.40||^2.8.33||~3.3.15||^3.4.3||^4.0.3", - "symfony/dependency-injection": "~2.1||~3.0||~4.0", - "symfony/event-dispatcher": "~2.1||~3.0||~4.0", - "symfony/translation": "~2.3||~3.0||~4.0", - "symfony/yaml": "~2.1||~3.0||~4.0" + "php": "^7.2 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/translation": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "herrera-io/box": "~1.6.1", - "phpunit/phpunit": "^4.8.36|^6.3", - "symfony/process": "~2.5|~3.0|~4.0" + "phpspec/prophecy": "^1.15", + "phpunit/phpunit": "^8.5 || ^9.0", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "^4.8" + }, + "suggest": { + "ext-dom": "Needed to output test results in JUnit format." }, "bin": [ "bin/behat" @@ -4363,13 +5295,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.5.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { - "psr-0": { - "Behat\\Behat": "src/", - "Behat\\Testwork": "src/" + "psr-4": { + "Behat\\Hook\\": "src/Behat/Hook/", + "Behat\\Step\\": "src/Behat/Step/", + "Behat\\Behat\\": "src/Behat/Behat/", + "Behat\\Testwork\\": "src/Behat/Testwork/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4383,7 +5317,7 @@ "homepage": "http://everzet.com" } ], - "description": "Scenario-oriented BDD framework for PHP 5.3", + "description": "Scenario-oriented BDD framework for PHP", "homepage": "http://behat.org/", "keywords": [ "Agile", @@ -4399,29 +5333,33 @@ "symfony", "testing" ], - "time": "2018-08-10T18:56:51+00:00" + "support": { + "issues": "https://github.com/Behat/Behat/issues", + "source": "https://github.com/Behat/Behat/tree/v3.14.0" + }, + "time": "2023-12-09T13:55:02+00:00" }, { "name": "behat/gherkin", - "version": "v4.6.0", + "version": "v4.9.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", "shasum": "" }, "require": { - "php": ">=5.3.1" + "php": "~7.2|~8.0" }, "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3|~4", - "symfony/yaml": "~2.3|~3|~4" + "cucumber/cucumber": "dev-gherkin-22.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/yaml": "~3|~4|~5" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -4429,7 +5367,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -4448,7 +5386,7 @@ "homepage": "http://everzet.com" } ], - "description": "Gherkin DSL parser for PHP 5.3", + "description": "Gherkin DSL parser for PHP", "homepage": "http://behat.org/", "keywords": [ "BDD", @@ -4458,39 +5396,47 @@ "gherkin", "parser" ], - "time": "2019-01-16T14:22:17+00:00" + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + }, + "time": "2021-10-12T13:05:09+00:00" }, { "name": "behat/mink", - "version": "v1.7.1", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/minkphp/Mink.git", - "reference": "e6930b9c74693dff7f4e58577e1b1743399f3ff9" + "reference": "d8527fdf8785aad38455fb426af457ab9937aece" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/Mink/zipball/e6930b9c74693dff7f4e58577e1b1743399f3ff9", - "reference": "e6930b9c74693dff7f4e58577e1b1743399f3ff9", + "url": "https://api.github.com/repos/minkphp/Mink/zipball/d8527fdf8785aad38455fb426af457ab9937aece", + "reference": "d8527fdf8785aad38455fb426af457ab9937aece", "shasum": "" }, "require": { - "php": ">=5.3.1", - "symfony/css-selector": "~2.1|~3.0" + "php": ">=7.2", + "symfony/css-selector": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0" + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^8.5.22 || ^9.5.11", + "symfony/error-handler": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" }, "suggest": { - "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", - "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation", + "behat/mink-browserkit-driver": "fast headless driver for any app without JS emulation", "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", - "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)" + "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)", + "dmore/chrome-mink-driver": "fast and JS-enabled driver for any app (requires chromium or google chrome)" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -4510,42 +5456,54 @@ } ], "description": "Browser controller/emulator abstraction for PHP", - "homepage": "http://mink.behat.org/", + "homepage": "https://mink.behat.org/", "keywords": [ "browser", "testing", "web" ], - "time": "2016-03-05T08:26:18+00:00" + "support": { + "issues": "https://github.com/minkphp/Mink/issues", + "source": "https://github.com/minkphp/Mink/tree/v1.11.0" + }, + "time": "2023-12-09T11:23:23+00:00" }, { "name": "behat/mink-browserkit-driver", - "version": "1.3.3", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/minkphp/MinkBrowserKitDriver.git", - "reference": "1b9a7ce903cfdaaec5fb32bfdbb26118343662eb" + "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/1b9a7ce903cfdaaec5fb32bfdbb26118343662eb", - "reference": "1b9a7ce903cfdaaec5fb32bfdbb26118343662eb", + "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/16d53476e42827ed3aafbfa4fde17a1743eafd50", + "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50", "shasum": "" }, "require": { - "behat/mink": "^1.7.1@dev", - "php": ">=5.3.6", - "symfony/browser-kit": "~2.3|~3.0|~4.0", - "symfony/dom-crawler": "~2.3|~3.0|~4.0" + "behat/mink": "^1.11.0@dev", + "ext-dom": "*", + "php": ">=7.2", + "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "mink/driver-testsuite": "dev-master", - "symfony/http-kernel": "~2.3|~3.0|~4.0" + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/error-handler": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/http-client": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/mime": "^4.4 || ^5.0 || ^6.0 || ^7.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "mink-driver", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -4565,48 +5523,154 @@ } ], "description": "Symfony2 BrowserKit driver for Mink framework", - "homepage": "http://mink.behat.org/", + "homepage": "https://mink.behat.org/", "keywords": [ "Mink", "Symfony2", "browser", "testing" ], - "time": "2018-05-02T09:25:31+00:00" + "support": { + "issues": "https://github.com/minkphp/MinkBrowserKitDriver/issues", + "source": "https://github.com/minkphp/MinkBrowserKitDriver/tree/v2.2.0" + }, + "time": "2023-12-09T11:30:50+00:00" }, { - "name": "behat/mink-extension", - "version": "2.3.1", + "name": "behat/transliterator", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/Behat/MinkExtension.git", - "reference": "80f7849ba53867181b7e412df9210e12fba50177" + "url": "https://github.com/Behat/Transliterator.git", + "reference": "baac5873bac3749887d28ab68e2f74db3a4408af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/MinkExtension/zipball/80f7849ba53867181b7e412df9210e12fba50177", - "reference": "80f7849ba53867181b7e412df9210e12fba50177", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/baac5873bac3749887d28ab68e2f74db3a4408af", + "reference": "baac5873bac3749887d28ab68e2f74db3a4408af", "shasum": "" }, "require": { - "behat/behat": "^3.0.5", - "behat/mink": "^1.5", - "php": ">=5.3.2", - "symfony/config": "^2.7|^3.0|^4.0" + "php": ">=7.2" }, "require-dev": { - "behat/mink-goutte-driver": "^1.1", - "phpspec/phpspec": "^2.0" + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0", + "phpunit/phpunit": "^8.5.25 || ^9.5.19" }, - "type": "behat-extension", + "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Behat\\MinkExtension": "src/" + "psr-4": { + "Behat\\Transliterator\\": "src/Behat/Transliterator" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "support": { + "issues": "https://github.com/Behat/Transliterator/issues", + "source": "https://github.com/Behat/Transliterator/tree/v1.5.0" + }, + "time": "2022-03-30T09:27:43+00:00" + }, + { + "name": "codelytv/coding-style", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/CodelyTV/php-coding_style-codely.git", + "reference": "41d7e6b651619467b05018666606a1ef0958263e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CodelyTV/php-coding_style-codely/zipball/41d7e6b651619467b05018666606a1ef0958263e", + "reference": "41d7e6b651619467b05018666606a1ef0958263e", + "shasum": "" + }, + "require": { + "symplify/easy-coding-standard": "^12.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "CodelyTv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Codely", + "homepage": "https://codely.com" + } + ], + "description": "PHP Coding Style rules we use in Codely", + "keywords": [ + "Code style", + "static analysis" + ], + "support": { + "issues": "https://github.com/CodelyTV/php-coding_style-codely/issues", + "source": "https://github.com/CodelyTV/php-coding_style-codely/tree/1.3.0" + }, + "funding": [ + { + "url": "https://bit.ly/CodelyTvPro", + "type": "custom" + } + ], + "time": "2024-08-05T14:17:14+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" } }, "notification-url": "https://packagist.org/downloads/", @@ -4615,88 +5679,218 @@ ], "authors": [ { - "name": "Christophe Coevoet", - "email": "stof@notk.org" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" }, { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" } ], - "description": "Mink extension for Behat", - "homepage": "http://extensions.behat.org/mink", + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "composer/pcre", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.8" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "browser", - "gui", - "test", - "web" + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.2.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } ], - "time": "2018-02-06T15:36:30+00:00" + "time": "2024-07-25T09:36:02+00:00" }, { - "name": "behat/transliterator", - "version": "v1.2.0", + "name": "composer/semver", + "version": "3.4.2", "source": { "type": "git", - "url": "https://github.com/Behat/Transliterator.git", - "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" + "url": "https://github.com/composer/semver.git", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", - "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "chuyskywalker/rolling-curl": "^3.1", - "php-yaoi/php-yaoi": "^1.0" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "psr-0": { - "Behat\\Transliterator": "src/" + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Artistic-1.0" - ], - "description": "String transliterator", - "keywords": [ - "i18n", - "slug", - "transliterator" ], - "time": "2017-04-04T11:38:05+00:00" + "time": "2024-07-12T11:35:52+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.2", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "d17708133b6c276d6e42ef887a877866b909d892" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/d17708133b6c276d6e42ef887a877866b909d892", - "reference": "d17708133b6c276d6e42ef887a877866b909d892", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -4714,132 +5908,106 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2019-01-28T20:25:53+00:00" - }, - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, - "type": "library", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "friends-of-behat/symfony-extension", - "version": "v2.0.8", + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", "source": { "type": "git", - "url": "https://github.com/FriendsOfBehat/SymfonyExtension.git", - "reference": "47ecdcc9a022775cbb612345b43962a9963cfb65" + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfBehat/SymfonyExtension/zipball/47ecdcc9a022775cbb612345b43962a9963cfb65", - "reference": "47ecdcc9a022775cbb612345b43962a9963cfb65", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", "shasum": "" }, "require": { - "behat/behat": "^3.4", - "php": "^7.1", - "symfony/dependency-injection": "^3.4|^4.1", - "symfony/http-kernel": "^3.4|^4.1", - "symfony/proxy-manager-bridge": "^3.4|^4.1" + "php": ">=5.3.2" }, "require-dev": { - "behat/mink": "^1.7", - "behat/mink-browserkit-driver": "^1.3", - "behat/mink-extension": "^2.2", - "behat/mink-selenium2-driver": "^1.3", - "friends-of-behat/service-container-extension": "^1.0", - "phpstan/phpstan-shim": "^0.11", - "sylius-labs/coding-standard": "^3.0", - "symfony/framework-bundle": "^3.4|^4.1", - "symfony/yaml": "^3.4|^4.1" - }, - "suggest": { - "behat/mink-browserkit-driver": "^1.3" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" }, + "type": "library", "autoload": { "psr-4": { - "FriendsOfBehat\\SymfonyExtension\\": "src/" + "XdgBaseDir\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Kamil Kokot", - "email": "kamil@kokot.me", - "homepage": "http://kamil.kokot.me" - } - ], - "description": "Integrates Behat with Symfony.", - "time": "2019-03-21T16:26:33+00:00" + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" }, { - "name": "fzaninotto/faker", - "version": "v1.8.0", + "name": "fakerphp/faker", + "version": "v1.23.1", "source": { "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", "ext-intl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." }, + "type": "library", "autoload": { "psr-4": { "Faker\\": "src/Faker/" @@ -4860,76 +6028,78 @@ "faker", "fixtures" ], - "time": "2018-07-12T10:23:15+00:00" + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + }, + "time": "2024-01-02T13:46:09+00:00" }, { - "name": "hamcrest/hamcrest-php", - "version": "v2.0.0", + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", "source": { "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", "shasum": "" }, "require": { - "php": "^5.3|^7.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" }, "require-dev": { - "phpunit/php-file-iterator": "1.3.3", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^1.0" + "phpunit/phpunit": "^7.0 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "hamcrest" - ] + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "ISC" ], - "description": "This is the PHP port of Hamcrest Matchers", - "keywords": [ - "test" + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } ], - "time": "2016-01-20T08:20:44+00:00" + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" }, { - "name": "jean85/pretty-package-versions", - "version": "1.2", + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", "source": { "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48" + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/75c7effcf3f77501d0e0caa75111aff4daa0dd48", - "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", "shasum": "" }, "require": { - "ocramius/package-versions": "^1.2.0", - "php": "^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" }, "type": "library", "extra": { @@ -4939,741 +6109,684 @@ }, "autoload": { "psr-4": { - "Jean85\\": "src/" + "LanguageServerProtocol\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "ISC" ], "authors": [ { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" + "name": "Felix Becker", + "email": "felix.b@outlook.com" } ], - "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "description": "PHP classes for the Language Server Protocol", "keywords": [ - "composer", - "package", - "release", - "versions" + "language", + "microsoft", + "php", + "server" ], - "time": "2018-06-13T13:22:40+00:00" + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" }, { - "name": "mockery/mockery", - "version": "1.2.2", + "name": "fidry/cpu-core-counter", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2", - "reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "~2.0", - "lib-pcre": ">=7.0", - "php": ">=5.6.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { - "psr-0": { - "Mockery": "library/" + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "PΓ‘draic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "name": "ThΓ©o FIDRY", + "email": "theo.fidry@gmail.com" } ], - "description": "Mockery is a simple yet flexible PHP mock object framework", - "homepage": "https://github.com/mockery/mockery", + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", - "test", - "test double", - "testing" + "CPU", + "core" ], - "time": "2019-02-13T09:37:52+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.9.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "78af75148f9fdd34ea727c8b529a9b4a8f7b740c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/78af75148f9fdd34ea727c8b529a9b4a8f7b740c", - "reference": "78af75148f9fdd34ea727c8b529a9b4a8f7b740c", - "shasum": "" - }, - "require": { - "php": "^7.2" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } ], - "time": "2018-10-30T00:14:44+00:00" + "time": "2024-02-07T09:43:46+00:00" }, { - "name": "nette/bootstrap", - "version": "v3.0.0", + "name": "friends-of-behat/mink-extension", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/nette/bootstrap.git", - "reference": "e1075af05c211915e03e0c86542f3ba5433df4a3" + "url": "https://github.com/FriendsOfBehat/MinkExtension.git", + "reference": "854336030e11983f580f49faad1b49a1238f9846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/bootstrap/zipball/e1075af05c211915e03e0c86542f3ba5433df4a3", - "reference": "e1075af05c211915e03e0c86542f3ba5433df4a3", + "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/854336030e11983f580f49faad1b49a1238f9846", + "reference": "854336030e11983f580f49faad1b49a1238f9846", "shasum": "" }, "require": { - "nette/di": "^3.0", - "nette/utils": "^3.0", - "php": ">=7.1" + "behat/behat": "^3.0.5", + "behat/mink": "^1.5", + "php": ">=7.4", + "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, - "require-dev": { - "latte/latte": "^2.2", - "nette/application": "^3.0", - "nette/caching": "^3.0", - "nette/database": "^3.0", - "nette/forms": "^3.0", - "nette/http": "^3.0", - "nette/mail": "^3.0", - "nette/robot-loader": "^3.0", - "nette/safe-stream": "^2.2", - "nette/security": "^3.0", - "nette/tester": "^2.0", - "tracy/tracy": "^2.6" + "replace": { + "behat/mink-extension": "self.version" }, - "suggest": { - "nette/robot-loader": "to use Configurator::createRobotLoader()", - "tracy/tracy": "to use Configurator::enableTracy()" + "require-dev": { + "behat/mink-goutte-driver": "^1.1 || ^2.0", + "phpspec/phpspec": "^6.0 || ^7.0 || 7.1.x-dev" }, - "type": "library", + "type": "behat-extension", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-0": { + "Behat\\MinkExtension": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "MIT" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com" }, { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Christophe Coevoet", + "email": "stof@notk.org" } ], - "description": "πŸ…± Nette Bootstrap: the simple way to configure and bootstrap your Nette application.", - "homepage": "https://nette.org", + "description": "Mink extension for Behat", + "homepage": "http://extensions.behat.org/mink", "keywords": [ - "bootstrapping", - "configurator", - "nette" + "browser", + "gui", + "test", + "web" ], - "time": "2019-03-26T12:59:07+00:00" + "support": { + "issues": "https://github.com/FriendsOfBehat/MinkExtension/issues", + "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.5" + }, + "time": "2024-01-11T09:12:02+00:00" }, { - "name": "nette/di", - "version": "v3.0.0", + "name": "friends-of-behat/symfony-extension", + "version": "v2.6.0", "source": { "type": "git", - "url": "https://github.com/nette/di.git", - "reference": "19d83539245aaacb59470828919182411061841f" + "url": "https://github.com/FriendsOfBehat/SymfonyExtension.git", + "reference": "dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/di/zipball/19d83539245aaacb59470828919182411061841f", - "reference": "19d83539245aaacb59470828919182411061841f", + "url": "https://api.github.com/repos/FriendsOfBehat/SymfonyExtension/zipball/dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd", + "reference": "dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "nette/neon": "^3.0", - "nette/php-generator": "^3.2.2", - "nette/robot-loader": "^3.2", - "nette/schema": "^1.0", - "nette/utils": "^3.0", - "php": ">=7.1" - }, - "conflict": { - "nette/bootstrap": "<3.0" + "behat/behat": "^3.6.1", + "php": "^8.1", + "symfony/dependency-injection": "^6.2 || ^7.0", + "symfony/http-kernel": "^6.2 || ^7.0" }, "require-dev": { - "nette/tester": "^2.2", - "tracy/tracy": "^2.3" + "behat/mink": "^1.9", + "behat/mink-browserkit-driver": "^2.0", + "behat/mink-selenium2-driver": "^1.3", + "friends-of-behat/mink-extension": "^2.5", + "friends-of-behat/page-object-extension": "^0.3.2", + "friends-of-behat/service-container-extension": "^1.1", + "sylius-labs/coding-standard": ">=4.1.1, <=4.2.1", + "symfony/browser-kit": "^6.2 || ^7.0", + "symfony/framework-bundle": "^6.2 || ^7.0", + "symfony/process": "^6.2 || ^7.0", + "symfony/yaml": "^6.2 || ^7.0", + "vimeo/psalm": "4.30.0" }, - "type": "library", + "suggest": { + "behat/mink": "^1.9", + "behat/mink-browserkit-driver": "^2.0", + "friends-of-behat/mink-extension": "^2.5" + }, + "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.2-dev" } }, "autoload": { - "classmap": [ - "src/" - ], - "files": [ - "src/compatibility.php" - ] + "psr-4": { + "FriendsOfBehat\\SymfonyExtension\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "MIT" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Kamil Kokot", + "email": "kamil@kokot.me", + "homepage": "https://kamilkokot.com" } ], - "description": "πŸ’Ž Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP 7.1 features.", - "homepage": "https://nette.org", - "keywords": [ - "compiled", - "di", - "dic", - "factory", - "ioc", - "nette", - "static" - ], - "time": "2019-04-03T19:35:46+00:00" + "description": "Integrates Behat with Symfony.", + "support": { + "issues": "https://github.com/FriendsOfBehat/SymfonyExtension/issues", + "source": "https://github.com/FriendsOfBehat/SymfonyExtension/tree/v2.6.0" + }, + "time": "2024-07-03T15:49:43+00:00" }, { - "name": "nette/finder", - "version": "v2.5.0", + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/nette/finder.git", - "reference": "6be1b83ea68ac558aff189d640abe242e0306fe2" + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/finder/zipball/6be1b83ea68ac558aff189d640abe242e0306fe2", - "reference": "6be1b83ea68ac558aff189d640abe242e0306fe2", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { - "nette/utils": "^2.4 || ~3.0.0", - "php": ">=7.1" + "php": "^5.3|^7.0|^8.0" }, - "conflict": { - "nette/nette": "<2.2" + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" }, "require-dev": { - "nette/tester": "^2.0", - "tracy/tracy": "^2.3" + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.1-dev" } }, "autoload": { "classmap": [ - "src/" + "hamcrest" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" - ], - "authors": [ - { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" - } + "BSD-3-Clause" ], - "description": "? Nette Finder: find files and directories with an intuitive API.", - "homepage": "https://nette.org", + "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ - "filesystem", - "glob", - "iterator", - "nette" + "test" ], - "time": "2019-02-28T18:13:25+00:00" + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" }, { - "name": "nette/neon", - "version": "v3.0.0", + "name": "masterminds/html5", + "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/nette/neon.git", - "reference": "cbff32059cbdd8720deccf9e9eace6ee516f02eb" + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/neon/zipball/cbff32059cbdd8720deccf9e9eace6ee516f02eb", - "reference": "cbff32059cbdd8720deccf9e9eace6ee516f02eb", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", "shasum": "" }, "require": { - "ext-iconv": "*", - "ext-json": "*", - "php": ">=7.0" + "ext-dom": "*", + "php": ">=5.3.0" }, "require-dev": { - "nette/tester": "^2.0", - "tracy/tracy": "^2.3" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Masterminds\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "MIT" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" }, { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], - "description": "🍸 Nette NEON: encodes and decodes NEON file format.", - "homepage": "http://ne-on.org", + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", "keywords": [ - "export", - "import", - "neon", - "nette", - "yaml" + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" ], - "time": "2019-02-05T21:30:40+00:00" + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + }, + "time": "2024-03-31T07:05:07+00:00" }, { - "name": "nette/php-generator", - "version": "v3.2.2", + "name": "mockery/mockery", + "version": "1.6.12", "source": { "type": "git", - "url": "https://github.com/nette/php-generator.git", - "reference": "acff8b136fad84b860a626d133e791f95781f9f5" + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/php-generator/zipball/acff8b136fad84b860a626d133e791f95781f9f5", - "reference": "acff8b136fad84b860a626d133e791f95781f9f5", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { - "nette/utils": "^2.4.2 || ~3.0.0", - "php": ">=7.1" + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" }, "require-dev": { - "nette/tester": "^2.0", - "tracy/tracy": "^2.3" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "BSD-3-Clause" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" + "name": "PΓ‘draic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" }, { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], - "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.3 features.", - "homepage": "https://nette.org", + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", "keywords": [ - "code", - "nette", - "php", - "scaffolding" + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" ], - "time": "2019-03-15T03:41:13+00:00" + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" }, { - "name": "nette/robot-loader", - "version": "v3.2.0", + "name": "myclabs/deep-copy", + "version": "1.12.0", "source": { "type": "git", - "url": "https://github.com/nette/robot-loader.git", - "reference": "0712a0e39ae7956d6a94c0ab6ad41aa842544b5c" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/robot-loader/zipball/0712a0e39ae7956d6a94c0ab6ad41aa842544b5c", - "reference": "0712a0e39ae7956d6a94c0ab6ad41aa842544b5c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "nette/finder": "^2.5", - "nette/utils": "^3.0", - "php": ">=7.1" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "nette/tester": "^2.0", - "tracy/tracy": "^2.3" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "MIT" ], - "authors": [ - { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" } ], - "description": "? Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.", - "homepage": "https://nette.org", - "keywords": [ - "autoload", - "class", - "interface", - "nette", - "trait" - ], - "time": "2019-03-08T21:57:24+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { - "name": "nette/schema", - "version": "v1.0.0", + "name": "netresearch/jsonmapper", + "version": "v4.4.1", "source": { "type": "git", - "url": "https://github.com/nette/schema.git", - "reference": "6241d8d4da39e825dd6cb5bfbe4242912f4d7e4d" + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/6241d8d4da39e825dd6cb5bfbe4242912f4d7e4d", - "reference": "6241d8d4da39e825dd6cb5bfbe4242912f4d7e4d", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", "shasum": "" }, "require": { - "nette/utils": "^3.0.1", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", "php": ">=7.1" }, "require-dev": { - "nette/tester": "^2.2", - "tracy/tracy": "^2.3" + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-0": { + "JsonMapper": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "OSL-3.0" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" } ], - "description": "πŸ“ Nette Schema: validating data structures against a given Schema.", - "homepage": "https://nette.org", - "keywords": [ - "config", - "nette" - ], - "time": "2019-04-03T15:53:25+00:00" + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + }, + "time": "2024-01-31T06:18:54+00:00" }, { - "name": "nette/utils", - "version": "v3.0.1", + "name": "nikic/php-parser", + "version": "v4.19.1", "source": { "type": "git", - "url": "https://github.com/nette/utils.git", - "reference": "bd961f49b211997202bda1d0fbc410905be370d4" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/bd961f49b211997202bda1d0fbc410905be370d4", - "reference": "bd961f49b211997202bda1d0fbc410905be370d4", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", "shasum": "" }, "require": { + "ext-tokenizer": "*", "php": ">=7.1" }, "require-dev": { - "nette/tester": "~2.0", - "tracy/tracy": "^2.3" - }, - "suggest": { - "ext-gd": "to use Image", - "ext-iconv": "to use Strings::webalize() and toAscii()", - "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", - "ext-json": "to use Nette\\Utils\\Json", - "ext-mbstring": "to use Strings::lower() etc...", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.9-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "BSD-3-Clause" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Nikita Popov" } ], - "description": "πŸ›  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", - "homepage": "https://nette.org", + "description": "A PHP parser written in PHP", "keywords": [ - "array", - "core", - "datetime", - "images", - "json", - "nette", - "paginator", - "password", - "slugify", - "string", - "unicode", - "utf-8", - "utility", - "validation" + "parser", + "php" ], - "time": "2019-03-22T01:00:30+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.2.1", + "name": "pdepend/pdepend", + "version": "2.16.2", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "5221f49a608808c1e4d436df32884cbc1b821ac0" + "url": "https://github.com/pdepend/pdepend.git", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/5221f49a608808c1e4d436df32884cbc1b821ac0", - "reference": "5221f49a608808c1e4d436df32884cbc1b821ac0", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" }, "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.0" + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "squizlabs/php_codesniffer": "^2.0.0" }, "bin": [ - "bin/php-parse" + "src/bin/pdepend" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "PDepend\\": "src/main/php/PDepend" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ + "description": "Official version of pdepend to be handled with Composer", + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" + }, + "funding": [ { - "name": "Nikita Popov" + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" } ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2019-02-16T20:54:15+00:00" + "time": "2023-12-17T18:09:59+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -5703,24 +6816,34 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -5750,39 +6873,95 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "name": "phpat/phpat", + "version": "0.10.18", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "url": "https://github.com/carlosas/phpat.git", + "reference": "4c29e330fb306876bca3174aa4b097d0d8611964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/carlosas/phpat/zipball/4c29e330fb306876bca3174aa4b097d0d8611964", + "reference": "4c29e330fb306876bca3174aa4b097d0d8611964", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "friendsofphp/php-cs-fixer": "3.46", + "kubawerlos/php-cs-fixer-custom-fixers": "3.18", + "phpunit/phpunit": "^9.0 || ^10.0", + "vimeo/psalm": "^5.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "PHPat\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carlos Alandete Sastre", + "email": "carlos.alandete@gmail.com" + } + ], + "description": "PHP Architecture Tester", + "support": { + "issues": "https://github.com/carlosas/phpat/issues", + "source": "https://github.com/carlosas/phpat/tree/0.10.18" + }, + "time": "2024-07-05T14:56:19+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5804,44 +6983,53 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "5.4.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5852,44 +7040,58 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + }, + "time": "2024-05-21T05:55:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5902,180 +7104,129 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" }, { - "name": "phpspec/prophecy", - "version": "1.8.0", + "name": "phpmd/phpmd", + "version": "2.15.0", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "url": "https://github.com/phpmd/phpmd.git", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", + "ext-xml": "*", + "pdepend/pdepend": "^2.16.1", + "php": ">=5.3.9" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "ext-json": "*", + "ext-simplexml": "*", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.8", + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" }, + "bin": [ + "src/bin/phpmd" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, "autoload": { "psr-0": { - "Prophecy\\": "src/" + "PHPMD\\": "src/main/php" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Marc WΓΌrth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "https://phpmd.org/", "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" + "dev", + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" ], - "time": "2018-08-05T17:53:17+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "2cc49f47c69b023eaf05b48e6529389893b13d74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/2cc49f47c69b023eaf05b48e6529389893b13d74", - "reference": "2cc49f47c69b023eaf05b48e6529389893b13d74", - "shasum": "" - }, - "require": { - "php": "~7.1" - }, - "require-dev": { - "consistence/coding-standard": "^2.0.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "phing/phing": "^2.16.0", - "phpstan/phpstan": "^0.10", - "phpunit/phpunit": "^6.3", - "slevomat/coding-standard": "^3.3.0", - "symfony/process": "^3.4 || ^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.3-dev" - } + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" }, - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "time": "2019-01-14T12:26:23+00:00" + "time": "2023-12-11T08:22:20+00:00" }, { - "name": "phpstan/phpstan", - "version": "0.10.8", + "name": "phpstan/phpdoc-parser", + "version": "1.29.1", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "4f828460a0276180da76c670a0a6e592e7c38b71" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4f828460a0276180da76c670a0a6e592e7c38b71", - "reference": "4f828460a0276180da76c670a0a6e592e7c38b71", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { - "composer/xdebug-handler": "^1.3.0", - "jean85/pretty-package-versions": "^1.0.3", - "nette/bootstrap": "^2.4 || ^3.0", - "nette/di": "^2.4.7 || ^3.0", - "nette/robot-loader": "^3.0.1", - "nette/utils": "^2.4.5 || ^3.0", - "nikic/php-parser": "^4.0.2", - "php": "~7.1", - "phpstan/phpdoc-parser": "^0.3", - "symfony/console": "~3.2 || ~4.0", - "symfony/finder": "~3.2 || ~4.0" - }, - "conflict": { - "symfony/console": "3.4.16 || 4.1.5" + "php": "^7.2 || ^8.0" }, "require-dev": { - "brianium/paratest": "^2.0", - "consistence/coding-standard": "^3.5", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", - "ext-gd": "*", - "ext-intl": "*", - "ext-mysqli": "*", - "ext-zip": "*", - "jakub-onderka/php-parallel-lint": "^1.0", - "localheinz/composer-normalize": "~0.9.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-deprecation-rules": "^0.10.2", - "phpstan/phpstan-php-parser": "^0.10", - "phpstan/phpstan-phpunit": "^0.10", - "phpstan/phpstan-strict-rules": "^0.10", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.7.2", - "squizlabs/php_codesniffer": "^3.3.2" + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" }, - "bin": [ - "bin/phpstan" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.10-dev" - } - }, "autoload": { "psr-4": { - "PHPStan\\": [ - "src/", - "build/PHPStan" + "PHPStan\\PhpDocParser\\": [ + "src/" ] } }, @@ -6083,94 +7234,111 @@ "license": [ "MIT" ], - "description": "PHPStan - PHP Static Analysis Tool", - "time": "2019-01-08T09:51:19+00:00" + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + }, + "time": "2024-05-31T08:52:43+00:00" }, { - "name": "phpstan/phpstan-mockery", - "version": "0.10.2", + "name": "phpstan/phpstan", + "version": "1.11.9", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan-mockery.git", - "reference": "14d568b6b56c957c9ae5330603303a367b21f0e9" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "e370bcddadaede0c1716338b262346f40d296f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/14d568b6b56c957c9ae5330603303a367b21f0e9", - "reference": "14d568b6b56c957c9ae5330603303a367b21f0e9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", + "reference": "e370bcddadaede0c1716338b262346f40d296f82", "shasum": "" }, "require": { - "nikic/php-parser": "^4.0", - "php": "~7.1", - "phpstan/phpdoc-parser": "^0.3", - "phpstan/phpstan": "^0.10.3" + "php": "^7.2|^8.0" }, - "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", - "jakub-onderka/php-parallel-lint": "^1.0", - "mockery/mockery": "^1.1", - "phing/phing": "^2.16.0", - "phpstan/phpstan-phpunit": "^0.10", - "phpstan/phpstan-strict-rules": "^0.10", - "phpunit/phpunit": "^7.2", - "slevomat/coding-standard": "^4.6.3" + "conflict": { + "phpstan/phpstan-shim": "*" }, + "bin": [ + "phpstan", + "phpstan.phar" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.10-dev" - } - }, "autoload": { - "psr-4": { - "PHPStan\\": "src/" - } + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHPStan Mockery extension", - "time": "2018-09-09T15:22:28+00:00" + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-08-01T16:25:18+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.3.2", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -6196,29 +7364,43 @@ "testing", "xunit" ], - "time": "2018-04-06T15:36:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -6233,7 +7415,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -6243,26 +7425,48 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -6279,37 +7483,47 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.9", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -6324,42 +7538,51 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "2.0.2", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -6374,65 +7597,73 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-11-27T05:48:46+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.14", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" }, "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -6440,10 +7671,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "9.6-dev" } }, "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], "classmap": [ "src/" ] @@ -6466,286 +7700,1215 @@ "testing", "xunit" ], - "time": "2019-02-01T05:22:47+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-07-10T11:45:39+00:00" + }, + { + "name": "psalm/plugin-mockery", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-mockery.git", + "reference": "876247d15f91df08240d00dac69c5135b6689283" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-mockery/zipball/876247d15f91df08240d00dac69c5135b6689283", + "reference": "876247d15f91df08240d00dac69c5135b6689283", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "mockery/mockery": "^1.0", + "php": "~7.4 || ~8.0 || ~8.1 || ~8.2", + "vimeo/psalm": "dev-master || ^5.0@rc || ^5.0" + }, + "require-dev": { + "codeception/codeception": "^4.1.9", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.13.1" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\MockeryPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\MockeryPlugin\\": [ + "." + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for Mockery", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-mockery/issues", + "source": "https://github.com/psalm/psalm-plugin-mockery/tree/1.1.0" + }, + "time": "2022-11-25T07:16:18+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", + "name": "psalm/plugin-phpunit", + "version": "0.18.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "vimeo/psalm": "dev-master || dev-4.x || ^4.7.1 || ^5@beta || ^5.0" }, "conflict": { - "phpunit/phpunit": "<6.0" + "phpunit/phpunit": "<7.5" + }, + "require-dev": { + "codeception/codeception": "^4.0.3", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.11.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for PHPUnit", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4" + }, + "time": "2022-12-03T07:47:07+00:00" + }, + { + "name": "psalm/plugin-symfony", + "version": "v5.2.5", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-symfony.git", + "reference": "fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196", + "reference": "fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php": "^8.1", + "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "^5.16" }, "require-dev": { - "phpunit/phpunit": "^6.5.11" + "doctrine/annotations": "^1.8|^2", + "doctrine/orm": "^2.9", + "phpunit/phpunit": "~7.5 || ~9.5", + "symfony/cache-contracts": "^1.0 || ^2.0", + "symfony/console": "*", + "symfony/form": "^5.0 || ^6.0 || ^7.0", + "symfony/messenger": "^5.0 || ^6.0 || ^7.0", + "symfony/security-core": "*", + "symfony/serializer": "^5.0 || ^6.0 || ^7.0", + "symfony/validator": "*", + "twig/twig": "^2.10 || ^3.0", + "weirdan/codeception-psalm-module": "dev-master" }, "suggest": { - "ext-soap": "*" + "weirdan/doctrine-psalm-plugin": "If Doctrine is used, it is recommended install this plugin" }, - "type": "library", + "type": "psalm-plugin", "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" + "psalm": { + "pluginClass": "Psalm\\SymfonyPsalmPlugin\\Plugin" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psalm\\SymfonyPsalmPlugin\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Farhad Safarov", + "email": "farhad.safarov@gmail.com" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "description": "Psalm Plugin for Symfony", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", + "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.2.5" + }, + "time": "2024-07-03T11:57:02+00:00" + }, + { + "name": "rector/rector", + "version": "0.18.13", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/f8011a76d36aa4f839f60f3b4f97707d97176618", + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "phpstan/phpstan": "^1.10.35" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", "keywords": [ - "mock", - "xunit" + "automation", + "dev", + "migration", + "refactoring" ], - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/0.18.13" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2023-12-20T16:08:01+00:00" }, { "name": "roave/security-advisories", "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "ff7456939acba6dd515a8a10aad66be6bc1b8dc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ff7456939acba6dd515a8a10aad66be6bc1b8dc1", + "reference": "ff7456939acba6dd515a8a10aad66be6bc1b8dc1", + "shasum": "" + }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.12", + "admidio/admidio": "<4.3.10", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", + "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.04.6", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", + "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9", + "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", + "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", + "airesvsg/acf-to-rest-api": "<=3.1", + "akaunting/akaunting": "<2.1.13", + "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", + "alextselegidis/easyappointments": "<1.5", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<1.0.1", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "aws/aws-sdk-php": ">=3,<3.2.1", + "amphp/http": "<=1.7.2|>=2,<=2.1", + "amphp/http-client": ">=4,<4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "apache-solr-for-typo3/solr": "<2.8.3", + "apereo/phpcas": "<1.6", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", + "appwrite/server-ce": "<=1.2.1", + "arc/web": "<3", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "artesaos/seotools": "<0.17.2", + "asymmetricrypt/asymmetricrypt": "<9.9.99", + "athlon1600/php-proxy": "<=5.1", + "athlon1600/php-proxy-app": "<=3", + "austintoddj/canvas": "<=3.4.2", + "auth0/wordpress": "<=4.6", + "automad/automad": "<=2.0.0.0-alpha5", + "automattic/jetpack": "<9.8", + "awesome-support/awesome-support": "<=6.0.7", + "aws/aws-sdk-php": "<3.288.1", + "azuracast/azuracast": "<0.18.3", + "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "backpack/crud": "<3.4.9", + "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", + "badaso/core": "<2.7", + "bagisto/bagisto": "<2.1", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.2", + "barzahlen/barzahlen-php": "<2.0.1", + "baserproject/basercms": "<5.0.9", + "bassjobsen/bootstrap-3-typeahead": ">4.0.2", + "bbpress/bbpress": "<2.6.5", + "bcosca/fatfree": "<3.7.2", + "bedita/bedita": "<4", + "bigfork/silverstripe-form-capture": ">=3,<3.1.1", + "billz/raspap-webgui": "<=3.1.4", + "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "blueimp/jquery-file-upload": "==6.4.4", + "bmarshall511/wordpress_zero_spam": "<5.2.13", + "bolt/bolt": "<3.7.2", + "bolt/core": "<=4.2", + "born05/craft-twofactorauthentication": "<3.3.4", + "bottelet/flarepoint": "<2.2.1", + "bref/bref": "<2.1.17", "brightlocal/phpwhois": "<=4.2.5", + "brotkrueml/codehighlight": "<2.7", + "brotkrueml/schema": "<1.13.1|>=2,<2.5.1", + "brotkrueml/typo3-matomo-integration": "<1.3.2", + "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4", + "bytefury/crater": "<6.0.2", + "cachethq/cachet": "<2.5.1", + "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", + "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", + "cardgate/magento2": "<2.0.33", + "cardgate/woocommerce": "<=3.1.15", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", - "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1.0.0-alpha11", + "catfan/medoo": "<1.7.5", + "causal/oidc": "<2.1", + "cecil/cecil": "<7.47.1", + "centreon/centreon": "<22.10.15", + "cesnet/simplesamlphp-module-proxystatistics": "<3.1", + "chriskacerguis/codeigniter-restserver": "<=2.7.1", + "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", + "ckeditor/ckeditor": "<4.24", + "cockpit-hq/cockpit": "<2.7|==2.7", + "codeception/codeception": "<3.1.3|>=4,<4.1.22", + "codeigniter/framework": "<3.1.9", + "codeigniter4/framework": "<4.4.7", + "codeigniter4/shield": "<1.0.0.0-beta8", + "codiad/codiad": "<=2.8.4", + "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", + "concrete5/concrete5": "<=9.3.2", + "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.35", - "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", - "contao/listing-bundle": ">=4,<4.4.8", - "contao/newsletter-bundle": ">=4,<4.1", + "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/core": "<3.5.39", + "contao/core-bundle": "<4.13.40|>=5,<5.3.4", + "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", + "contao/managed-edition": "<=1.5", + "corveda/phpsandbox": "<1.3.5", + "cosenary/instagram": "<=2.3", + "craftcms/cms": "<4.6.2|>=5.0.0.0-beta1,<=5.2.2", + "croogo/croogo": "<4", + "cuyz/valinor": "<0.12", + "czproject/git-php": "<4.0.3", + "dapphp/securimage": "<3.6.6", + "darylldoyle/safe-svg": "<1.9.10", + "datadog/dd-trace": ">=0.30,<0.30.2", + "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", - "doctrine/annotations": ">=1,<1.2.7", + "dbrisinajumi/d2files": "<1", + "dcat/laravel-admin": "<=2.1.3.0-beta", + "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", + "desperado/xml-bundle": "<=0.1.7", + "devgroup/dotplant": "<2020.09.14-dev", + "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "doctrine/annotations": "<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", + "doctrine/common": "<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.64|>=8,<8.5.13|>=8.6,<8.6.12", - "drupal/drupal": ">=7,<7.64|>=8,<8.5.13|>=8.6,<8.6.12", + "doctrine/doctrine-module": "<0.7.2", + "doctrine/mongodb-odm": "<1.0.2", + "doctrine/mongodb-odm-bundle": "<3.0.1", + "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<19.0.2", + "dompdf/dompdf": "<2.0.4", + "doublethreedigital/guest-entries": "<3.1.2", + "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.1.8|>=10.2,<10.2.2", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "duncanmcclean/guest-entries": "<3.1.2", + "dweeves/magmi": "<=0.7.24", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", + "ecodev/newsletter": "<=4", + "ectouch/ectouch": "<=2.7.2", + "egroupware/egroupware": "<23.1.20240624", + "elefant/cms": "<2.0.7", + "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "elijaa/phpmemcacheadmin": "<=1.3", + "encore/laravel-admin": "<=1.8.19", + "endroid/qr-code-bundle": "<3.4.2", + "enhavo/enhavo-app": "<=0.13.1", + "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", - "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.13.1|>=6,<6.7.9.1|>=6.8,<6.13.5.1|>=7,<7.2.4.1|>=7.3,<7.3.2.1", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.12.3|>=2011,<2017.12.4.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3", - "ezsystems/repository-forms": ">=2.3,<2.3.2.1", + "ether/logs": "<3.0.4", + "evolutioncms/evolution": "<=3.2.3", + "exceedone/exment": "<4.4.3|>=5,<5.0.3", + "exceedone/laravel-admin": "<2.2.3|==3", + "ezsystems/demobundle": ">=5.4,<5.4.6.1-dev", + "ezsystems/ez-support-tools": ">=2.2,<2.2.3", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", + "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", + "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", + "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1-dev", + "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", + "ezsystems/ezplatform-user": ">=1,<1.0.1", + "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", "ezyang/htmlpurifier": "<4.1.1", - "firebase/php-jwt": "<2", + "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "facturascripts/facturascripts": "<=2022.08", + "fastly/magento2": "<1.2.26", + "feehi/cms": "<=2.1.1", + "feehi/feehicms": "<=2.1.1", + "fenom/fenom": "<=2.12.1", + "filegator/filegator": "<7.8", + "filp/whoops": "<2.1.13", + "fineuploader/php-traditional-server": "<=1.2.2", + "firebase/php-jwt": "<6", + "fisharebest/webtrees": "<=2.1.18", + "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", + "fixpunkt/fp-newsletter": "<1.1.1|>=2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<1.8.5", + "flarum/flarum": "<0.1.0.0-beta8", + "flarum/framework": "<1.8.5", + "flarum/mentions": "<1.6.3", + "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", + "flarum/tags": "<=0.1.0.0-beta13", + "floriangaerber/magnesium": "<0.3.1", + "fluidtypo3/vhs": "<5.1.1", + "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", + "fof/upload": "<1.2.3", + "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", "fooman/tcpdf": "<6.2.22", + "forkcms/forkcms": "<5.11.1", "fossar/tcpdf-parser": "<6.2.22", + "francoisjacquet/rosariosis": "<=11.5.1", + "frappant/frp-form-answers": "<3.1.2|>=4,<4.0.2", + "friendsofsymfony/oauth2-php": "<1.3", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "friendsofsymfony/user-bundle": ">=1,<1.3.5", + "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", + "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", + "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", + "froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3", + "froxlor/froxlor": "<2.1.9", + "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", - "gree/jose": "<=2.2", + "funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3", + "gaoming13/wechat-php-sdk": "<=1.10.2", + "genix/cms": "<=1.1.11", + "getformwork/formwork": "<1.13.1|==2.0.0.0-beta1", + "getgrav/grav": "<1.7.46", + "getkirby/cms": "<4.1.1", + "getkirby/kirby": "<=2.5.12", + "getkirby/panel": "<2.5.14", + "getkirby/starterkit": "<=3.7.0.2", + "gilacms/gila": "<=1.15.4", + "gleez/cms": "<=1.3|==2", + "globalpayments/php-sdk": "<2", + "gogentooss/samlbase": "<1.2.7", + "google/protobuf": "<3.15", + "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", - "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", - "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "grumpydictator/firefly-iii": "<6.1.17", + "gugoan/economizzer": "<=0.9.0.0-beta1", + "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", + "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", + "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", + "harvesthq/chosen": "<1.8.7", + "helloxz/imgurl": "<=2.31", + "hhxsv5/laravel-s": "<3.7.36", + "hillelcoren/invoice-ninja": "<5.3.35", + "himiklab/yii2-jqgrid-widget": "<1.0.8", + "hjue/justwriting": "<=1", + "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "httpsoft/http-message": "<1.0.12", + "hyn/multi-tenant": ">=5.6,<5.7.2", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6.0.0-beta1,<4.6.9", + "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", + "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", + "ibexa/post-install": "<=1.0.4", + "ibexa/solr": ">=4.5,<4.5.4", + "ibexa/user": ">=4,<4.4.3", + "icecoder/icecoder": "<=8.1", + "idno/known": "<=1.3.1", + "ilicmiljan/secure-props": ">=1.2,<1.2.2", + "illuminate/auth": "<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", + "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "imdbphp/imdbphp": "<=5.1.1", + "impresscms/impresscms": "<=1.4.5", + "impresspages/impresspages": "<=1.0.12", + "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3", + "in2code/ipandlanguageredirect": "<5.1.2", + "in2code/lux": "<17.6.1|>=18,<24.0.2", + "innologi/typo3-appointments": "<2.0.6", + "intelliants/subrion": "<4.2.2", + "inter-mediator/inter-mediator": "==5.5", + "ipl/web": "<0.10.1", + "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", - "james-heinrich/getid3": "<1.9.9", + "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "james-heinrich/getid3": "<1.9.21", + "james-heinrich/phpthumb": "<1.7.12", + "jasig/phpcas": "<1.3.3", + "jcbrand/converse.js": "<3.3.3", + "johnbillion/wp-crontrol": "<1.16.2", + "joomla/application": "<1.0.13", + "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/filesystem": "<1.6.2|>=2,<2.0.1", + "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", + "joomla/input": ">=2,<2.0.2", + "joomla/joomla-cms": ">=2.5,<3.9.12", "joomla/session": "<1.3.1", + "joyqi/hyper-down": "<=2.4.27", + "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", + "juzaweb/cms": "<=3.4", + "jweiland/events2": "<8.3.8|>=9,<9.0.6", "kazist/phpwhois": "<=4.2.6", + "kelvinmo/simplexrd": "<3.1.1", + "kevinpapst/kimai2": "<1.16.7", + "khodakhah/nodcms": "<=3", + "kimai/kimai": "<2.16", + "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", + "klaviyo/magento2-extension": ">=1,<3", + "knplabs/knp-snappy": "<=1.4.2", + "kohana/core": "<3.3.3", + "krayin/laravel-crm": "<1.2.2", "kreait/firebase-php": ">=3.2,<3.8.1", + "kumbiaphp/kumbiapp": "<=1.1.1", "la-haute-societe/tcpdf": "<6.2.22", - "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2", + "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", + "laminas/laminas-http": "<2.14.2", + "laravel/fortify": "<1.11.1", + "laravel/framework": "<6.20.44|>=7,<7.30.6|>=8,<8.75", + "laravel/laravel": ">=5.4,<5.4.22", + "laravel/socialite": ">=1,<2.0.10", + "latte/latte": "<2.10.8", + "lavalite/cms": "<=9|==10.1", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", "league/commonmark": "<0.18.3", - "magento/magento1ce": "<1.9.4", - "magento/magento1ee": ">=1.9,<1.14.4", - "magento/product-community-edition": ">=2,<2.2.7", + "league/flysystem": "<1.1.4|>=2,<2.1.1", + "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", + "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", + "libreform/libreform": ">=2,<=2.0.8", + "librenms/librenms": "<2017.08.18", + "liftkit/database": "<2.13.2", + "lightsaml/lightsaml": "<1.3.5", + "limesurvey/limesurvey": "<3.27.19", + "livehelperchat/livehelperchat": "<=3.91", + "livewire/livewire": ">2.2.4,<2.2.6|>=3.3.5,<3.4.9", + "lms/routes": "<2.1.1", + "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "luyadev/yii-helpers": "<1.2.1", + "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch8|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch6|==2.4.7", + "magento/core": "<=1.9.4.5", + "magento/magento1ce": "<1.9.4.3-dev", + "magento/magento1ee": ">=1,<1.14.4.3-dev", + "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", + "magneto/core": "<1.9.4.4-dev", + "maikuolan/phpmussel": ">=1,<1.6", + "mainwp/mainwp": "<=4.4.3.3", + "mantisbt/mantisbt": "<2.26.2", + "marcwillmann/turn": "<0.3.3", + "matyhtf/framework": "<3.0.6", + "mautic/core": "<4.4.12|>=5.0.0.0-alpha,<5.0.4", + "mdanter/ecc": "<2", + "mediawiki/core": "<1.36.2", + "mediawiki/matomo": "<2.4.3", + "mediawiki/semantic-media-wiki": "<4.0.2", + "melisplatform/melis-asset-manager": "<5.0.1", + "melisplatform/melis-cms": "<5.0.1", + "melisplatform/melis-front": "<5.0.1", + "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", + "mgallegos/laravel-jqgrid": "<=1.3", + "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", + "microsoft/microsoft-graph-beta": "<2.0.1", + "microsoft/microsoft-graph-core": "<2.0.2", + "microweber/microweber": "<=2.0.4", + "mikehaertl/php-shellcommand": "<1.6.1", + "miniorange/miniorange-saml": "<1.4.3", + "mittwald/typo3_forum": "<1.2.1", + "mobiledetect/mobiledetectlib": "<2.8.32", + "modx/revolution": "<=2.8.3.0-patch", + "mojo42/jirafeau": "<4.4", + "mongodb/mongodb": ">=1,<1.9.2", "monolog/monolog": ">=1.8,<1.12", + "moodle/moodle": "<4.3.5|>=4.4.0.0-beta,<4.4.1", + "mos/cimage": "<0.7.19", + "movim/moxl": ">=0.8,<=0.10", + "movingbytes/social-network": "<=1.2.1", + "mpdf/mpdf": "<=7.1.7", + "munkireport/comment": "<4.1", + "munkireport/managedinstalls": "<2.6", + "munkireport/munki_facts": "<1.5", + "munkireport/munkireport": ">=2.5.3,<5.6.3", + "munkireport/reportdata": "<3.5", + "munkireport/softwareupdate": "<1.6", + "mustache/mustache": ">=2,<2.14.1", "namshi/jose": "<2.2", + "neoan3-apps/template": "<1.1.1", + "neorazorx/facturascripts": "<2022.04", + "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", + "neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9", + "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", + "neos/swiftmailer": "<5.4.5", + "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", + "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", + "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "nilsteampassnet/teampass": "<3.0.10", + "nonfiction/nterchange": "<4.1.1", + "notrinos/notrinos-erp": "<=0.7", + "noumo/easyii": "<=0.9", + "novaksolutions/infusionsoft-php-sdk": "<1", + "nukeviet/nukeviet": "<4.5.02", + "nyholm/psr7": "<1.6.1", + "nystudio107/craft-seomatic": "<3.4.12", + "nzedb/nzedb": "<0.8", + "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", + "october/backend": "<1.1.2", + "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", + "october/october": "<=3.4.4", + "october/rain": "<1.0.472|>=1.1,<1.1.2", + "october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15", + "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.10.4", + "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", + "open-web-analytics/open-web-analytics": "<1.7.4", + "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "oro/crm": ">=1.7,<1.7.4", - "oro/platform": ">=1.7,<1.7.4", + "openmage/magento-lts": "<20.10.1", + "opensolutions/vimbadmin": "<=3.0.15", + "opensource-workshop/connect-cms": "<1.7.2|>=2,<2.3.2", + "orchid/platform": ">=9,<9.4.4|>=14.0.0.0-alpha4,<14.5", + "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", + "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", + "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", + "oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1", + "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", + "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", + "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", + "oxid-esales/oxideshop-ce": "<4.5", + "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", + "packbackbooks/lti-1-3-php-library": "<5", "padraic/humbug_get_contents": "<1.1.2", - "pagarme/pagarme-php": ">=0,<3", + "pagarme/pagarme-php": "<3", + "pagekit/pagekit": "<=1.0.18", + "paragonie/ecc": "<2.0.1", "paragonie/random_compat": "<2", + "passbolt/passbolt_api": "<4.6.2", + "paypal/adaptivepayments-sdk-php": "<=3.9.2", + "paypal/invoice-sdk-php": "<=3.9", "paypal/merchant-sdk-php": "<3.12", - "pear/archive_tar": "<1.4.4", - "phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6", - "phpoffice/phpexcel": "<=1.8.1", - "phpoffice/phpspreadsheet": "<=1.5", + "paypal/permissions-sdk-php": "<=3.9.1", + "pear/archive_tar": "<1.4.14", + "pear/auth": "<1.2.4", + "pear/crypt_gpg": "<1.6.7", + "pear/pear": "<=1.10.1", + "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", + "personnummer/personnummer": "<3.0.2", + "phanan/koel": "<5.1.4", + "phenx/php-svg-lib": "<0.5.2", + "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", + "php-mod/curl": "<2.3.2", + "phpbb/phpbb": "<3.2.10|>=3.3,<3.3.1", + "phpems/phpems": ">=6,<=6.1.3", + "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", + "phpmailer/phpmailer": "<6.5", + "phpmussel/phpmussel": ">=1,<1.6", + "phpmyadmin/phpmyadmin": "<5.2.1", + "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5", + "phpoffice/common": "<0.2.9", + "phpoffice/phpexcel": "<1.8", + "phpoffice/phpspreadsheet": "<1.16", + "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", + "phpservermon/phpservermon": "<3.6", + "phpsysinfo/phpsysinfo": "<3.4.3", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", - "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", + "phpxmlrpc/phpxmlrpc": "<4.9.2", + "pi/pi": "<=2.5", + "pimcore/admin-ui-classic-bundle": "<=1.5.1", + "pimcore/customer-management-framework-bundle": "<4.0.6", + "pimcore/data-hub": "<1.2.4", + "pimcore/demo": "<10.3", + "pimcore/ecommerce-framework-bundle": "<1.0.10", + "pimcore/perspective-editor": "<1.5.1", + "pimcore/pimcore": "<11.2.4", + "pixelfed/pixelfed": "<0.11.11", + "plotly/plotly.js": "<2.25.2", + "pocketmine/bedrock-protocol": "<8.0.2", + "pocketmine/pocketmine-mp": "<5.11.2", + "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", + "pressbooks/pressbooks": "<5.18", + "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/blockreassurance": "<=5.1.3", + "prestashop/blockwishlist": ">=2,<2.1.1", + "prestashop/contactform": ">=1.0.1,<4.3", + "prestashop/gamification": "<2.3.2", + "prestashop/prestashop": "<8.1.6", + "prestashop/productcomments": "<5.0.2", + "prestashop/ps_emailsubscription": "<2.6.1", + "prestashop/ps_facetedsearch": "<3.4.1", + "prestashop/ps_linklist": "<3.1", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", + "processwire/processwire": "<=3.0.229", + "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", + "pterodactyl/panel": "<1.11.6", + "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", + "ptrofimov/beanstalk_console": "<1.7.14", + "pubnub/pubnub": "<6.1", "pusher/pusher-php-server": "<2.2.1", - "robrichards/xmlseclibs": ">=1,<3.0.2", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "pwweb/laravel-core": "<=0.3.6.0-beta", + "pyrocms/pyrocms": "<=3.9.1", + "qcubed/qcubed": "<=3.1.1", + "quickapps/cms": "<=2.0.0.0-beta2", + "rainlab/blog-plugin": "<1.4.1", + "rainlab/debugbar-plugin": "<3.1", + "rainlab/user-plugin": "<=1.4.5", + "rankmath/seo-by-rank-math": "<=1.0.95", + "rap2hpoutre/laravel-log-viewer": "<0.13", + "react/http": ">=0.7,<1.9", + "really-simple-plugins/complianz-gdpr": "<6.4.2", + "redaxo/source": "<=5.15.1", + "remdex/livehelperchat": "<4.29", + "reportico-web/reportico": "<=8.1", + "rhukster/dom-sanitizer": "<1.0.7", + "rmccue/requests": ">=1.6,<1.8", + "robrichards/xmlseclibs": ">=1,<3.0.4", + "roots/soil": "<4.1", + "rudloff/alltube": "<3.0.3", + "s-cart/core": "<6.9", + "s-cart/s-cart": "<6.9", + "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", + "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", + "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", - "shopware/shopware": "<5.3.7", - "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", + "sfroemken/url_redirect": "<=1.2.1", + "sheng/yiicms": "<=1.2", + "shopware/core": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", + "shopware/platform": "<6.5.8.8-dev|>=6.6.0.0-RC1-dev,<6.6.1", + "shopware/production": "<=6.3.5.2", + "shopware/shopware": "<=5.7.17", + "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopxo/shopxo": "<=6.1", + "showdoc/showdoc": "<2.10.4", + "silverstripe-australia/advancedreports": ">=1,<=2", + "silverstripe/admin": "<1.13.19|>=2,<2.1.8", + "silverstripe/assets": ">=1,<1.11.1", + "silverstripe/cms": "<4.11.3", + "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": ">=3,<3.6.7|>=3.7,<3.7.3|>=4,<4.0.7|>=4.1,<4.1.5|>=4.2,<4.2.4|>=4.3,<4.3.1", - "silverstripe/userforms": "<3", + "silverstripe/framework": "<5.2.16", + "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", + "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", + "silverstripe/recipe-cms": ">=4.5,<4.5.3", + "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/reports": "<5.2.3", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", + "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", + "silverstripe/subsites": ">=2,<2.6.1", + "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", + "silverstripe/userforms": "<3|>=5,<5.4.2", + "silverstripe/versioned-admin": ">=1,<1.11.1", "simple-updates/phpwhois": "<=1", - "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", - "simplesamlphp/simplesamlphp": "<1.16.3", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4|==5.0.0.0-alpha12", + "simplesamlphp/simplesamlphp": "<1.18.6", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "simplesamlphp/simplesamlphp-module-openid": "<1", + "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", + "simplesamlphp/xml-security": "==1.6.11", + "simplito/elliptic-php": "<1.0.6", + "sitegeist/fluid-components": "<3.5", + "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", + "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", "slim/slim": "<2.6", - "smarty/smarty": "<3.1.33", + "slub/slub-events": "<3.0.3", + "smarty/smarty": "<4.5.3|>=5,<5.1.1", + "snipe/snipe-it": "<6.4.2", "socalnick/scn-social-auth": "<1.15.2", + "socialiteproviders/steam": "<1.1", + "spatie/browsershot": "<3.57.4", + "spatie/image-optimizer": "<1.7.3", + "spipu/html2pdf": "<5.2.8", + "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "stormpath/sdk": ">=0,<9.9.99", - "swiftmailer/swiftmailer": ">=4,<5.4.5", + "ssddanbrown/bookstack": "<24.05.1", + "statamic/cms": "<4.46|>=5.3,<5.6.2", + "stormpath/sdk": "<9.9.99", + "studio-42/elfinder": "<=2.1.64", + "studiomitte/friendlycaptcha": "<0.1.4", + "subhh/libconnect": "<7.0.8|>=8,<8.1", + "sukohi/surpass": "<1", + "sulu/form-bundle": ">=2,<2.5.3", + "sulu/sulu": "<1.6.44|>=2,<2.4.17|>=2.5,<2.5.13", + "sumocoders/framework-user-bundle": "<1.4", + "superbig/craft-audit": "<3.0.2", + "swag/paypal": "<5.4.4", + "swiftmailer/swiftmailer": "<6.2.5", + "swiftyedit/swiftyedit": "<1.2", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", - "sylius/sylius": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", - "symfony/dependency-injection": ">=2,<2.0.17", + "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": "<1.10.1", + "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", + "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", + "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "symbiote/silverstripe-multivaluefield": ">=3,<3.1", + "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", + "symbiote/silverstripe-seed": "<6.0.3", + "symbiote/silverstripe-versionedfiles": "<=2.0.3", + "symfont/process": ">=0", + "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", + "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", + "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", + "symfony/mime": ">=4.3,<4.3.8", + "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/polyfill": ">=1,<1.10", "symfony/polyfill-php55": ">=1,<1.10", + "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.19|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", + "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", + "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", + "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2|>=5.4,<5.4.31|>=6,<6.3.8", + "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", + "symfony/symfony": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/translation": ">=2,<2.0.17", + "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", + "symfony/ux-autocomplete": "<2.11.2", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "tecnickcom/tcpdf": "<6.2.22", + "symfony/webhook": ">=6.3,<6.3.8", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7|>=2.2.0.0-beta1,<2.2.0.0-beta2", + "symphonycms/symphony-2": "<2.6.4", + "t3/dce": "<0.11.5|>=2.2,<2.6.2", + "t3g/svg-sanitizer": "<1.0.3", + "t3s/content-consent": "<1.0.3|>=2,<2.0.2", + "tastyigniter/tastyigniter": "<3.3", + "tcg/voyager": "<=1.4", + "tecnickcom/tcpdf": "<=6.7.4", + "terminal42/contao-tablelookupwizard": "<3.3.5", "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", + "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", - "titon/framework": ">=0,<9.9.99", + "thinkcmf/thinkcmf": "<6.0.8", + "thorsten/phpmyfaq": "<3.2.2", + "tikiwiki/tiki-manager": "<=17.1", + "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", + "tinymce/tinymce": "<7.2", + "tinymighty/wiki-seo": "<1.2.2", + "titon/framework": "<9.9.99", + "tobiasbg/tablepress": "<=2.0.0.0-RC1", + "topthink/framework": "<6.0.17|>=6.1,<6.1.5|>=8,<8.0.4", + "topthink/think": "<=6.1.1", + "topthink/thinkphp": "<=3.2.3", + "torrentpier/torrentpier": "<=2.4.3", + "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", + "tribalsystems/zenario": "<9.5.60602", "truckersmp/phpwhois": "<=4.3.1", - "twig/twig": "<1.38|>=2,<2.7", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.23|>=9,<9.5.4", - "typo3/cms-core": ">=8,<8.7.23|>=9,<9.5.4", - "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", - "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", + "ttskch/pagination-service-provider": "<1", + "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3", + "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "typo3/cms-core": "<=8.7.56|>=9,<=9.5.47|>=10,<=10.4.44|>=11,<=11.5.36|>=12,<=12.4.14|>=13,<=13.1", + "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", + "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", + "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", + "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8", + "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", + "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", "ua-parser/uap-php": "<3.8", + "uasoft-indonesia/badaso": "<=2.9.7", + "unisharp/laravel-filemanager": "<2.6.4", + "userfrosting/userfrosting": ">=0.3.1,<4.6.3", + "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", + "uvdesk/community-skeleton": "<=1.1.1", + "uvdesk/core-framework": "<=1.1.1", + "vanilla/safecurl": "<0.9.2", + "verbb/comments": "<1.5.5", + "verbb/formie": "<2.1.6", + "verbb/image-resizer": "<2.0.9", + "verbb/knock-knock": "<1.2.8", + "verot/class.upload.php": "<=2.1.6", + "villagedefrance/opencart-overclocked": "<=1.11.1", + "vova07/yii2-fileapi-widget": "<0.1.9", + "vrana/adminer": "<4.8.1", + "vufind/vufind": ">=2,<9.1.1", + "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", + "wallabag/wallabag": "<2.6.7", + "wanglelecc/laracms": "<=1.0.3", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", + "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-feet/coastercms": "==5.5", + "webbuilders-group/silverstripe-kapost-bridge": "<0.4", + "webcoast/deferred-image-processing": "<1.0.2", + "webklex/laravel-imap": "<5.3", + "webklex/php-imap": "<5.3", + "webpa/webpa": "<3.1.2", + "wikibase/wikibase": "<=1.39.3", + "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.15", + "winter/wn-backend-module": "<1.2.4", + "winter/wn-dusk-plugin": "<2.1", + "winter/wn-system-module": "<1.2.4", + "wintercms/winter": "<=1.2.3", + "woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3", + "wp-cli/wp-cli": ">=0.12,<2.5", + "wp-graphql/wp-graphql": "<=1.14.5", + "wp-premium/gravityforms": "<2.4.21", + "wpanel/wpanel4-cms": "<=4.3.1", + "wpcloud/wp-stateless": "<3.2", + "wpglobus/wpglobus": "<=1.9.6", + "wwbn/avideo": "<14.3", + "xataface/xataface": "<3", + "xpressengine/xpressengine": "<3.0.15", + "yab/quarx": "<2.4.5", + "yeswiki/yeswiki": "<4.1", + "yetiforce/yetiforce-crm": "<=6.4", + "yidashi/yii2cmf": "<=2", + "yii2mod/yii2-cms": "<1.9.2", + "yiisoft/yii": "<1.1.29", + "yiisoft/yii2": "<2.0.49.4-dev", + "yiisoft/yii2-authclient": "<2.2.15", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.15", + "yiisoft/yii2-dev": "<2.0.43", "yiisoft/yii2-elasticsearch": "<2.0.5", - "yiisoft/yii2-gii": "<2.0.4", + "yiisoft/yii2-gii": "<=2.2.4", "yiisoft/yii2-jui": "<2.0.4", "yiisoft/yii2-redis": "<2.0.8", + "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", + "yoast-seo-for-typo3/yoast_seo": "<7.2.3", + "yourls/yourls": "<=1.8.2", + "yuan1994/tpadmin": "<=1.3.12", + "zencart/zencart": "<=1.5.7.0-beta", + "zendesk/zendesk_api_client_php": "<2.2.11", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-db": "<2.2.10|>=2.3,<2.3.5", "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", - "zendframework/zend-diactoros": ">=1,<1.8.4", - "zendframework/zend-feed": ">=1,<2.10.3", + "zendframework/zend-diactoros": "<1.8.4", + "zendframework/zend-feed": "<2.10.3", "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=1,<2.8.1", + "zendframework/zend-http": "<2.8.1", "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-mail": "<2.4.11|>=2.5,<2.7.2", "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-session": ">=2,<2.2.9|>=2.3,<2.3.4", "zendframework/zend-validator": ">=2.3,<2.3.6", "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": "<2.5.1", + "zendframework/zendframework": "<=3", "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", + "zendframework/zendopenid": "<2.0.2", + "zendframework/zendrest": "<2.0.2", + "zendframework/zendservice-amazon": "<2.0.3", + "zendframework/zendservice-api": "<1", + "zendframework/zendservice-audioscrobbler": "<2.0.2", + "zendframework/zendservice-nirvanix": "<2.0.2", + "zendframework/zendservice-slideshare": "<2.0.2", + "zendframework/zendservice-technorati": "<2.0.2", + "zendframework/zendservice-windowsazure": "<2.0.2", "zendframework/zendxml": ">=1,<1.0.1", + "zenstruck/collection": "<0.2.1", "zetacomponents/mail": "<1.8.2", "zf-commons/zfc-user": "<1.2.2", "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", - "zfr/zfr-oauth2-server-module": "<0.1.2" + "zfr/zfr-oauth2-server-module": "<0.1.2", + "zoujingli/thinkadmin": "<=6.1.53" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2024-08-05T15:04:41+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] }, - "type": "metapackage", "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2019-04-07T10:25:46+00:00" + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -6765,34 +8928,44 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -6805,6 +8978,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -6816,10 +8993,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -6829,27 +9002,38 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" }, { - "name": "sebastian/diff", - "version": "2.0.1", + "name": "sebastian/complexity", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "php": "^7.0" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { @@ -6868,45 +9052,118 @@ ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -6931,34 +9188,44 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -6971,6 +9238,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -6979,46 +9250,55 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -7026,7 +9306,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -7049,34 +9329,101 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -7096,32 +9443,42 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -7141,32 +9498,42 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -7179,44 +9546,57 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -7234,31 +9614,43 @@ "email": "sebastian@phpunit.de" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/type", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -7277,68 +9669,46 @@ "role": "lead" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "2.9.2", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "2acf168de78487db620ab4bc524135a13cfe6745" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/2acf168de78487db620ab4bc524135a13cfe6745", - "reference": "2acf168de78487db620ab4bc524135a13cfe6745", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" + "php": ">=7.3" }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7347,56 +9717,58 @@ ], "authors": [ { - "name": "Greg Sherwood", + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-11-07T22:31:41+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "symfony/browser-kit", - "version": "v4.2.5", + "name": "spatie/array-to-xml", + "version": "3.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0" + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "ext-dom": "*", + "php": "^8.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "symfony/process": "" + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Spatie\\ArrayToXml\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7404,51 +9776,62 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.3.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/spatie", + "type": "github" } ], - "description": "Symfony BrowserKit Component", - "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "time": "2024-05-01T10:20:27+00:00" }, { - "name": "symfony/class-loader", - "version": "v3.4.24", + "name": "symfony/browser-kit", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/class-loader.git", - "reference": "4459eef5298dedfb69f771186a580062b8516497" + "url": "https://github.com/symfony/browser-kit.git", + "reference": "9c13742e3175b5815e272b981876ae329bec2040" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497", - "reference": "4459eef5298dedfb69f771186a580062b8516497", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9c13742e3175b5815e272b981876ae329bec2040", + "reference": "9c13742e3175b5815e272b981876ae329bec2040", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.2", + "symfony/dom-crawler": "^6.4|^7.0" }, "require-dev": { - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + "symfony/css-selector": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\ClassLoader\\": "" + "Symfony\\Component\\BrowserKit\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7468,33 +9851,45 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony ClassLoader Component", + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.24", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf" + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", - "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" @@ -7508,54 +9903,64 @@ "MIT" ], "authors": [ - { - "name": "Jean-FranΓ§ois Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, + { + "name": "Jean-FranΓ§ois Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony CssSelector Component", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.2.5", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb" + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/53c97769814c80a84a8403efcf3ae7ae966d53bb", - "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/01ce8174447f1f1dd33a5854b01beef79061d9fa", + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa", "shasum": "" }, "require": { - "php": "^7.1.3", + "masterminds/html5": "^2.6", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0" - }, - "suggest": { - "symfony/css-selector": "" + "symfony/css-selector": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" @@ -7578,52 +9983,81 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony DomCrawler Component", + "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/phpunit-bridge", - "version": "v4.2.5", + "name": "symfony/translation", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "8796da921e4613352818b478e9bb0803ea0dbb9a" + "url": "https://github.com/symfony/translation.git", + "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/8796da921e4613352818b478e9bb0803ea0dbb9a", - "reference": "8796da921e4613352818b478e9bb0803ea0dbb9a", + "url": "https://api.github.com/repos/symfony/translation/zipball/8d5e50c813ba2859a6dfc99a0765c550507934a1", + "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" }, - "suggest": { - "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + "provide": { + "symfony/translation-implementation": "2.3|3.0" }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - }, - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" }, + "type": "library", "autoload": { "files": [ - "bootstrap.php" + "Resources/functions.php" ], "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" + "Symfony\\Component\\Translation\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7635,214 +10069,285 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony PHPUnit Bridge", + "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", - "time": "2019-03-26T20:16:42+00:00" + "support": { + "source": "https://github.com/symfony/translation/tree/v7.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-26T12:41:01+00:00" }, { - "name": "symfony/proxy-manager-bridge", - "version": "v4.2.5", + "name": "symplify/easy-coding-standard", + "version": "12.3.4", "source": { "type": "git", - "url": "https://github.com/symfony/proxy-manager-bridge.git", - "reference": "5c0a3b921fbd5d6ca6cfc9321e67ebb2a8727ac3" + "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", + "reference": "03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/proxy-manager-bridge/zipball/5c0a3b921fbd5d6ca6cfc9321e67ebb2a8727ac3", - "reference": "5c0a3b921fbd5d6ca6cfc9321e67ebb2a8727ac3", + "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e", + "reference": "03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e", "shasum": "" }, "require": { - "ocramius/proxy-manager": "~2.1", - "php": "^7.1.3", - "symfony/dependency-injection": "~4.0" + "php": ">=7.2" }, "conflict": { - "zendframework/zend-eventmanager": "2.6.0" - }, - "require-dev": { - "symfony/config": "~3.4|~4.0" + "friendsofphp/php-cs-fixer": "<3.46", + "phpcsstandards/php_codesniffer": "<3.8", + "symplify/coding-standard": "<12.1" }, - "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } + "suggest": { + "ext-dom": "Needed to support checkstyle output format in class CheckstyleOutputFormatter" }, + "bin": [ + "bin/ecs" + ], + "type": "library", "autoload": { - "psr-4": { - "Symfony\\Bridge\\ProxyManager\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", + "keywords": [ + "Code style", + "automation", + "fixer", + "static analysis" + ], + "support": { + "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", + "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.3.4" + }, + "funding": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "url": "https://www.paypal.me/rectorphp", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/tomasvotruba", + "type": "github" } ], - "description": "Symfony ProxyManager Bridge", - "homepage": "https://symfony.com", - "time": "2019-02-23T15:42:05+00:00" + "time": "2024-08-01T07:55:09+00:00" }, { - "name": "symfony/translation", - "version": "v4.2.5", + "name": "theseer/tokenizer", + "version": "1.2.3", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "e46933cc31b68f51f7fc5470fb55550407520f56" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e46933cc31b68f51f7fc5470fb55550407520f56", - "reference": "e46933cc31b68f51f7fc5470fb55550407520f56", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0.2", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "symfony/translation-contracts-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/intl": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/theseer", + "type": "github" } ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com", - "time": "2019-04-01T14:13:08+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.1.2", + "name": "vimeo/psalm", + "version": "5.25.0", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + "url": "https://github.com/vimeo/psalm.git", + "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", + "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", "shasum": "" }, "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.16", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } }, - "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psalm\\": "src/Psalm/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Matthew Brown" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-04-04T09:56:43+00:00" + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2024-06-16T15:08:35+00:00" }, { "name": "webmozart/assert", - "version": "1.4.0", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -7866,28 +10371,30 @@ "check", "validate" ], - "time": "2018-12-25T11:19:39+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "RC", "stability-flags": { "roave/security-advisories": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.3", + "php": "^8.3", "ext-amqp": "*", "ext-apcu": "*", - "ext-bcmath": "*", - "ext-ctype": "*", - "ext-intl": "*", "ext-json": "*", "ext-zend-opcache": "*", "ext-pdo": "*" }, "platform-dev": { "ext-xdebug": "*" - } + }, + "plugin-api-version": "2.6.0" } diff --git a/databases/_pre_mooc.sql b/databases/_pre_mooc.sql deleted file mode 100644 index 03a1f0e5c..000000000 --- a/databases/_pre_mooc.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE DATABASE IF NOT EXISTS `video`; - -USE `video`; diff --git a/databases/mooc.sql b/databases/mooc.sql deleted file mode 100644 index d50f02bea..000000000 --- a/databases/mooc.sql +++ /dev/null @@ -1,48 +0,0 @@ -CREATE TABLE `videos` ( - `id` CHAR(36) NOT NULL, - `type` VARCHAR(32) NOT NULL, - `title` VARCHAR(255) NOT NULL, - `url` VARCHAR(255) NOT NULL, - `course_id` CHAR(36) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE `students` ( - `id` CHAR(36) NOT NULL, - `name` VARCHAR(155) NOT NULL, - `total_videos_created` INTEGER(5) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE `steps` ( - `id` CHAR(36) NOT NULL, - `type` tinyint(3) unsigned NOT NULL, - `lesson_id` CHAR(36) NOT NULL, - `title` VARCHAR(155) NOT NULL, - `estimated_duration` INTEGER(5) NOT NULL, - `step_order` INTEGER(5) NOT NULL, - `creation_date` timestamp NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE `step_challenges` ( - `id` CHAR(36) NOT NULL, - `statement` TEXT NOT NULL, - PRIMARY KEY (`id`), - CONSTRAINT `fk_steps_challenges__step_id` FOREIGN KEY (`id`) REFERENCES `steps` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE `step_quiz` ( - `id` CHAR(36) NOT NULL, - `questions` TEXT NOT NULL, - PRIMARY KEY (`id`), - CONSTRAINT `fk_steps_quiz__step_id` FOREIGN KEY (`id`) REFERENCES `steps` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE `step_video` ( - `id` CHAR(36) NOT NULL, - `url` VARCHAR(255) NOT NULL, - `text` TEXT NOT NULL, - PRIMARY KEY (`id`), - CONSTRAINT `fk_steps_video__step_id` FOREIGN KEY (`id`) REFERENCES `steps` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/docker-compose.yml b/docker-compose.yml index 22fde7377..19fc0366b 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,49 +1,107 @@ version: '3' services: - nginx: - container_name: codelytv-cqrs_ddd_php_example-nginx - image: nginx:1.15-alpine + shared_rabbitmq: + container_name: codely-php_ddd_skeleton-rabbitmq + image: 'rabbitmq:3.10.5-management' restart: unless-stopped ports: - - "8030:80" + - "5630:5672" + - "8090:15672" + environment: + - RABBITMQ_DEFAULT_USER=codely + - RABBITMQ_DEFAULT_PASS=c0d3ly + + shared_prometheus: + container_name: codely-php_ddd_skeleton-prometheus + image: prom/prometheus:v2.36.1 volumes: - - .:/app:delegated - - ./etc/infrastructure/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - depends_on: - - php + - ./etc/prometheus/:/etc/prometheus/ + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + ports: + - "9999:9090" + + mooc_mysql: + container_name: codely-php_ddd_skeleton-mooc-mysql + image: mariadb:10.7.4 + ports: + - "3360:3306" + environment: + - MYSQL_ROOT_PASSWORD= + - MYSQL_ALLOW_EMPTY_PASSWORD=yes + healthcheck: + test: ["CMD", "mysqladmin", "--user=root", "--password=", "--host=127.0.0.1", "ping", "--silent"] + interval: 2s + timeout: 10s + retries: 10 + command: ["--default-authentication-plugin=mysql_native_password"] - php: - container_name: codelytv-cqrs_ddd_php_example-php + backoffice_elasticsearch: + container_name: codely-php_ddd_skeleton-backoffice-elastic + image: docker.elastic.co/elasticsearch/elasticsearch:8.2.3 + ports: + - "9200:9200" + - "9300:9300" + environment: + - discovery.type=single-node + - xpack.security.enabled=false + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + + backoffice_backend_php: + container_name: codely-php_ddd_skeleton-backoffice_backend-php + user: "${UID}:${GID}" build: context: . dockerfile: Dockerfile restart: unless-stopped ports: - - "9030:9001" + - "8040:8040" + - "9040:9001" volumes: - .:/app:delegated - env_file: - - .env depends_on: - - mysql - - rabbitmq + - shared_rabbitmq + - shared_prometheus + - backoffice_elasticsearch + command: symfony serve --dir=apps/backoffice/backend/public --port=8040 - mysql: - container_name: codelytv-cqrs_ddd_php_example-mysql - image: mariadb:10.4 + backoffice_frontend_php: + container_name: codely-php_ddd_skeleton-backoffice_frontend-php + user: "${UID}:${GID}" + build: + context: . + dockerfile: Dockerfile restart: unless-stopped ports: - - "3330:3306" - env_file: - - .env + - "8041:8041" + - "9041:9001" + volumes: + - .:/app:delegated + depends_on: + - shared_rabbitmq + - shared_prometheus + - backoffice_elasticsearch + - mooc_mysql + command: symfony serve --dir=apps/backoffice/frontend/public --port=8041 - rabbitmq: - container_name: codelytv-cqrs_ddd_php_example-rabbitmq - image: 'rabbitmq:3.7-management' + mooc_backend_php: + container_name: codely-php_ddd_skeleton-mooc_backend-php + user: "${UID}:${GID}" + build: + context: . + dockerfile: Dockerfile restart: unless-stopped ports: - - 5630:5672 - - 15630:15672 - env_file: - - .env + - "8030:8030" + - "9030:9001" + volumes: + - .:/app:delegated + depends_on: + - shared_rabbitmq + - shared_prometheus + - mooc_mysql + command: symfony serve --dir=apps/mooc/backend/public --port=8030 diff --git a/ecs.php b/ecs.php new file mode 100644 index 000000000..e3ed961ba --- /dev/null +++ b/ecs.php @@ -0,0 +1,26 @@ +paths([__DIR__ . '/apps', __DIR__ . '/src', __DIR__ . '/tests', ]); + + $ecsConfig->sets([CodingStyle::DEFAULT]); + + $ecsConfig->skip([ + FinalClassFixer::class => [ + __DIR__ . '/apps/backoffice/backend/src/BackofficeBackendKernel.php', + __DIR__ . '/apps/backoffice/frontend/src/BackofficeFrontendKernel.php', + __DIR__ . '/apps/mooc/backend/src/MoocBackendKernel.php', + __DIR__ . '/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php', + ], + __DIR__ . '/apps/backoffice/backend/var', + __DIR__ . '/apps/backoffice/frontend/var', + __DIR__ . '/apps/mooc/backend/var', + __DIR__ . '/apps/mooc/frontend/var', + ]); +}; diff --git a/etc/consume.sh b/etc/consume.sh deleted file mode 100755 index b83a3b0ec..000000000 --- a/etc/consume.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -while : - do - ./../applications/api/bin/console codelytv:domain-events:consume increase_user_total_videos_created_on_video_created 100 - done - diff --git a/etc/databases/backoffice/courses.json b/etc/databases/backoffice/courses.json new file mode 100644 index 000000000..bbf66139e --- /dev/null +++ b/etc/databases/backoffice/courses.json @@ -0,0 +1,20 @@ +{ + "mappings": { + "courses": { + "properties": { + "id": { + "type": "keyword", + "index": true + }, + "name": { + "type": "text", + "index": true + }, + "duration": { + "type": "text", + "index": true + } + } + } + } +} diff --git a/etc/databases/mooc.sql b/etc/databases/mooc.sql new file mode 100644 index 000000000..1a094f827 --- /dev/null +++ b/etc/databases/mooc.sql @@ -0,0 +1,129 @@ +/* ------------------------- + MOOC CONTEXT +---------------------------- */ + +-- Generic tables + +CREATE TABLE mutations ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + table_name VARCHAR(255) NOT NULL, + operation ENUM ('INSERT', 'UPDATE', 'DELETE') NOT NULL, + old_value JSON NULL, + new_value JSON NULL, + mutation_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +CREATE TABLE domain_events ( + id CHAR(36) NOT NULL, + aggregate_id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + body JSON NOT NULL, + occurred_on TIMESTAMP NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +-- Aggregates tables + +CREATE TABLE courses ( + id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + duration VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +CREATE TRIGGER after_courses_insert + AFTER INSERT + ON courses + FOR EACH ROW +BEGIN + INSERT INTO mutations (table_name, operation, new_value, mutation_timestamp) + VALUES ('courses', 'INSERT', JSON_OBJECT('id', new.id, 'name', new.name, 'duration', new.duration), NOW()); +END; + +CREATE TRIGGER after_courses_update + AFTER UPDATE + ON courses + FOR EACH ROW +BEGIN + INSERT INTO mutations (table_name, operation, old_value, new_value, mutation_timestamp) + VALUES ('courses', + 'UPDATE', + JSON_OBJECT('id', old.id, 'name', old.name, 'duration', old.duration), + JSON_OBJECT('id', new.id, 'name', new.name, 'duration', new.duration), + NOW()); +END; + +CREATE TRIGGER after_courses_delete + AFTER DELETE + ON courses + FOR EACH ROW +BEGIN + INSERT INTO mutations (table_name, operation, old_value, mutation_timestamp) + VALUES ('courses', 'DELETE', JSON_OBJECT('id', old.id, 'name', old.name, 'duration', old.duration), NOW()); +END; + +CREATE TABLE courses_counter ( + id CHAR(36) NOT NULL, + total INT NOT NULL, + existing_courses JSON NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +INSERT INTO courses_counter (id, total, existing_courses) +VALUES ("cdf26d7d-3deb-4e8c-9f73-4ac085a8d6f3", 0, "[]"); + +CREATE TABLE steps ( + id CHAR(36) NOT NULL, + title VARCHAR(255) NOT NULL, + duration INT NOT NULL, + type VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +CREATE TABLE steps_video ( + id CHAR(36) NOT NULL, + url VARCHAR(255) NOT NULL, + FOREIGN KEY (id) REFERENCES steps(id) ON DELETE CASCADE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +CREATE TABLE steps_exercise ( + id CHAR(36) NOT NULL, + content VARCHAR(255) NOT NULL, + FOREIGN KEY (id) REFERENCES steps(id) ON DELETE CASCADE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + +CREATE TABLE steps_quiz ( + id CHAR(36) NOT NULL, + questions TEXT NOT NULL, + FOREIGN KEY (id) REFERENCES steps(id) ON DELETE CASCADE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + + +/* ------------------------- + BACKOFFICE CONTEXT +---------------------------- */ + +CREATE TABLE backoffice_courses ( + id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + duration VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; diff --git a/etc/endpoints/backoffice_frontend.http b/etc/endpoints/backoffice_frontend.http new file mode 100644 index 000000000..78b30022a --- /dev/null +++ b/etc/endpoints/backoffice_frontend.http @@ -0,0 +1,19 @@ +# ELASTIC - Search +POST localhost:9200/backoffice_courses/_search +Content-Type: application/json + +{ + "query": { + "term": { + "name": "Pepe" + } + } +} + +### +# ELASTIC - Search +POST localhost:9200/backoffice_courses/_search +Content-Type: application/json + +### + diff --git a/etc/endpoints/mooc_backend.http b/etc/endpoints/mooc_backend.http new file mode 100644 index 000000000..070955a99 --- /dev/null +++ b/etc/endpoints/mooc_backend.http @@ -0,0 +1,10 @@ +# Create a course +PUT localhost:8030/courses/{{$uuid}} +Content-Type: application/json + +{ + "name": "The best course", + "duration": "5 hours" +} + +### diff --git a/etc/hooks/install-hooks.sh b/etc/hooks/install-hooks.sh deleted file mode 100755 index 6a901d9a5..000000000 --- a/etc/hooks/install-hooks.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -cd "$(dirname "$0")/../.." - -rm -rf .git/hooks - -ln -s ../etc/hooks .git/hooks -chmod -R 777 etc/hooks/* diff --git a/etc/hooks/pre-push b/etc/hooks/pre-push deleted file mode 100755 index dcaa735d5..000000000 --- a/etc/hooks/pre-push +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Checks if locally staged changes are formatted properly ignoring non-staged changes. -# Install it with the `install-hooks.sh` script -# Based on: https://gist.github.com/cvogt/2676ed6c6d1abafa3d6a - -PATH=$PATH:/usr/local/bin:/usr/local/sbin - -echo "" -echo "Running pre-push hook… (you can omit this with --no-verify, but don't)" - -echo "* Moving to the project directory…" -_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -DIR=$( echo $_DIR | sed 's/\/.git\/hooks$//' ) - -echo "* Stashing non-staged changes so we avoid checking them…" -git diff --quiet -hadNoNonStagedChanges=$? - -if ! [ $hadNoNonStagedChanges -eq 0 ] -then - git stash --keep-index -u > /dev/null -fi - -echo "* Checking pre push conditions ('test' composer task)…" -composer test > /dev/null -canPush=$? - -if [ $canPush -ne 0 ] -then - echo " [KO] Error :(" -fi - -echo "* Applying the stash with the non-staged changes…" -if ! [ $hadNoNonStagedChanges -eq 0 ] -then - sleep 1 && git stash pop --index > /dev/null & # sleep because otherwise commit fails when this leads to a merge conflict -fi - -# Final result -echo "" - -if [ $canPush -eq 0 ] -then - echo "[OK] Your code will be pushed young Padawan" - exit 0 -else - echo "[KO] Cancelling push due to 'composer test' task error (run 'composer test' for more information)" - exit 1 -fi diff --git a/etc/http/status.http b/etc/http/status.http deleted file mode 100644 index 94f147f2e..000000000 --- a/etc/http/status.http +++ /dev/null @@ -1,3 +0,0 @@ -GET http://localhost:8030/status - -### diff --git a/etc/http/student.http b/etc/http/student.http deleted file mode 100644 index 96a1dea60..000000000 --- a/etc/http/student.http +++ /dev/null @@ -1,9 +0,0 @@ -PUT http://localhost:8030/students/75b14bd7-7925-424c-9a69-29b89d2b44f9 -Content-Type: application/json -{ -} - -### -GET http://localhost:8030/students/75b14bd7-7925-424c-9a69-29b89d2b44f9 - -### diff --git a/etc/infrastructure/nginx/default.conf b/etc/infrastructure/nginx/default.conf deleted file mode 100755 index 58a681ac3..000000000 --- a/etc/infrastructure/nginx/default.conf +++ /dev/null @@ -1,30 +0,0 @@ -server { - listen 80; - server_name localhost api.codelytv.localhost; - root /app/applications/mooc_backend/web; - - error_log stderr; - access_log stdout; - - rewrite ^/api\.php/?(.*)$ /$1 permanent; - - try_files $uri @rewriteapp; - - location @rewriteapp { - rewrite ^(.*)$ /api.php/$1 last; - } - - location ~ /\. { - deny all; - } - - location ~ ^/(api)\.php(/|$) { - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_index api.php; - send_timeout 1800; - fastcgi_read_timeout 1800; - fastcgi_pass php:9000; - } -} diff --git a/etc/infrastructure/php/conf.d/apcu.ini b/etc/infrastructure/php/conf.d/apcu.ini index 3fb1fc345..8fae9c5a0 100644 --- a/etc/infrastructure/php/conf.d/apcu.ini +++ b/etc/infrastructure/php/conf.d/apcu.ini @@ -1,3 +1,5 @@ +apc.enable_cli=1 +apc.enabled=1 apc.shm_segments=1 apc.shm_size=256M apc.num_files_hint=7000 diff --git a/etc/infrastructure/php/conf.d/xdebug.ini b/etc/infrastructure/php/conf.d/xdebug.ini index 8d7170a6e..d97b6a059 100644 --- a/etc/infrastructure/php/conf.d/xdebug.ini +++ b/etc/infrastructure/php/conf.d/xdebug.ini @@ -1,14 +1,15 @@ -zend_extension=xdebug.so +zend_extension = xdebug.so ;Debugging -xdebug.remote_enable = 1; -xdebug.remote_connect_back = 1; -xdebug.remote_autostart = 1; -xdebug.remote_port = 9001; +xdebug.mode=debug +xdebug.start_with_request = yes +xdebug.discover_client_host = yes +xdebug.client_port = 9001 +xdebug.client_host = host.docker.internal ;Profiling -xdebug.profiler_enable = 0; -xdebug.profiler_enable_trigger = 1; -xdebug.profiler_output_dir = "/tmp/xdebug"; +xdebug.mode=profile +xdebug.start_with_request=trigger -xdebug.max_nesting_level = 500; +xdebug.output_dir = "/tmp/xdebug" +xdebug.max_nesting_level = 500 diff --git a/etc/infrastructure/php/php.ini b/etc/infrastructure/php/php.ini index dd77c36da..21ab98fdd 100644 --- a/etc/infrastructure/php/php.ini +++ b/etc/infrastructure/php/php.ini @@ -1,4 +1,4 @@ date.timezone = "UTC" html_errors = "On" display_errors = "On" -error_reporting = E_ALL \ No newline at end of file +error_reporting = E_ALL diff --git a/etc/phpstan/phpstan.neon b/etc/phpstan/phpstan.neon deleted file mode 100644 index 34e294fb6..000000000 --- a/etc/phpstan/phpstan.neon +++ /dev/null @@ -1,13 +0,0 @@ -parameters: - bootstrap: %currentWorkingDirectory%/vendor/autoload.php - includes: - - %currentWorkingDirectory%/vendor/phpstan/phpstan-mockery/extension.neon - reportUnmatchedIgnoredErrors: false - ignoreErrors: - - '#Ternary operator condition is always true.#' - - '#Static call to instance method CodelyTv.*#' - - '#Strict comparison using === between.*#' - - '#Call to an undefined method .*::shouldReceive#' - - '#Call to an undefined method .*::shouldNotReceive#' - - '#Parameter .*MockInterface given.#' - - '#.*was not matched in reported errors.#' diff --git a/etc/prometheus/prometheus.yml b/etc/prometheus/prometheus.yml new file mode 100644 index 000000000..db505fd9d --- /dev/null +++ b/etc/prometheus/prometheus.yml @@ -0,0 +1,21 @@ +scrape_configs: + + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'backoffice_backend' + scrape_interval: 5s + static_configs: + - targets: ['codelytv-php_ddd_skeleton-backoffice_backend-php:8040'] + + - job_name: 'backoffice_frontend' + scrape_interval: 5s + static_configs: + - targets: ['codelytv-php_ddd_skeleton-backoffice_frontend-php:8041'] + + - job_name: 'mooc_backend' + scrape_interval: 5s + static_configs: + - targets: ['codelytv-php_ddd_skeleton-mooc_backend-php:8030'] diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 000000000..dee5bbe84 --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,48 @@ + + + + apps/*/*/var/* + *SimilarComparator* + *IsSimilar* + + src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php + + tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php + + tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..4404d69b2 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,25 @@ +includes: + - vendor/phpat/phpat/extension.neon + +parameters: + level: 0 + paths: + - ./apps + - ./src + - ./tests + excludePaths: + - ./apps/backoffice/backend/var + - ./apps/backoffice/frontend/var + - ./apps/mooc/backend/var + - ./apps/mooc/frontend/var + +services: + - + class: CodelyTv\Tests\Shared\SharedArchitectureTest + tags: + - phpat.test + + - + class: CodelyTv\Tests\Mooc\MoocArchitectureTest + tags: + - phpat.test diff --git a/phpunit.xml.dist b/phpunit.xml similarity index 72% rename from phpunit.xml.dist rename to phpunit.xml index 1cd153f28..06fda8b8b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml @@ -9,7 +9,7 @@ beStrictAboutChangesToGlobalState="true" beStrictAboutTestsThatDoNotTestAnything="false" beStrictAboutTodoAnnotatedTests="true" - bootstrap="./applications/bootstrap.php" + bootstrap="./apps/bootstrap.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" @@ -24,17 +24,19 @@ - + - - ./tests + + ./tests/Backoffice + + + ./tests/Mooc + + + ./tests/Shared - - - - diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 000000000..228b2ab7b --- /dev/null +++ b/psalm.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..7ec25f27b --- /dev/null +++ b/rector.php @@ -0,0 +1,27 @@ +paths([ + __DIR__ . '/apps', + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + + $rectorConfig->sets([ + LevelSetList::UP_TO_PHP_82, + SetList::TYPE_DECLARATION + ]); + + $rectorConfig->skip([ + __DIR__ . '/apps/backoffice/backend/var', + __DIR__ . '/apps/backoffice/frontend/var', + __DIR__ . '/apps/mooc/backend/var', + __DIR__ . '/apps/mooc/frontend/var', + ]); +}; diff --git a/src/Analytics/DomainEvents/Application/Store/DomainEventStorer.php b/src/Analytics/DomainEvents/Application/Store/DomainEventStorer.php new file mode 100644 index 000000000..22dfbb7ba --- /dev/null +++ b/src/Analytics/DomainEvents/Application/Store/DomainEventStorer.php @@ -0,0 +1,28 @@ +repository->save($domainEvent); + } +} diff --git a/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php b/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php new file mode 100644 index 000000000..5607d45ce --- /dev/null +++ b/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php @@ -0,0 +1,32 @@ +eventId()); + $aggregateId = new AnalyticsDomainEventAggregateId($event->aggregateId()); + $name = new AnalyticsDomainEventName($event::eventName()); + $body = new AnalyticsDomainEventBody($event->toPrimitives()); + + $this->storer->store($id, $aggregateId, $name, $body); + } +} diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php new file mode 100644 index 000000000..db3fe917e --- /dev/null +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php @@ -0,0 +1,15 @@ +value; + } +} diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php new file mode 100644 index 000000000..6cfebcb7a --- /dev/null +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php @@ -0,0 +1,9 @@ +username; + } + + public function password(): string + { + return $this->password; + } +} diff --git a/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandler.php b/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandler.php new file mode 100644 index 000000000..6278c023d --- /dev/null +++ b/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandler.php @@ -0,0 +1,22 @@ +username()); + $password = new AuthPassword($command->password()); + + $this->authenticator->authenticate($username, $password); + } +} diff --git a/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php b/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php new file mode 100644 index 000000000..99bcf5dfe --- /dev/null +++ b/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php @@ -0,0 +1,35 @@ +repository->search($username); + + if ($auth === null) { + throw new InvalidAuthUsername($username); + } + + $this->ensureCredentialsAreValid($auth, $password); + } + + private function ensureCredentialsAreValid(AuthUser $auth, AuthPassword $password): void + { + if (!$auth->passwordMatches($password)) { + throw new InvalidAuthCredentials($auth->username()); + } + } +} diff --git a/src/Backoffice/Auth/Domain/AuthPassword.php b/src/Backoffice/Auth/Domain/AuthPassword.php new file mode 100644 index 000000000..ec76483a7 --- /dev/null +++ b/src/Backoffice/Auth/Domain/AuthPassword.php @@ -0,0 +1,15 @@ +value() === $other->value(); + } +} diff --git a/src/Backoffice/Auth/Domain/AuthRepository.php b/src/Backoffice/Auth/Domain/AuthRepository.php new file mode 100644 index 000000000..49088ecd2 --- /dev/null +++ b/src/Backoffice/Auth/Domain/AuthRepository.php @@ -0,0 +1,10 @@ +password->isEquals($password); + } + + public function username(): AuthUsername + { + return $this->username; + } +} diff --git a/src/Backoffice/Auth/Domain/AuthUsername.php b/src/Backoffice/Auth/Domain/AuthUsername.php new file mode 100644 index 000000000..bc1f5b884 --- /dev/null +++ b/src/Backoffice/Auth/Domain/AuthUsername.php @@ -0,0 +1,9 @@ + are invalid', $username->value())); + } +} diff --git a/src/Backoffice/Auth/Domain/InvalidAuthUsername.php b/src/Backoffice/Auth/Domain/InvalidAuthUsername.php new file mode 100644 index 000000000..b120207cb --- /dev/null +++ b/src/Backoffice/Auth/Domain/InvalidAuthUsername.php @@ -0,0 +1,15 @@ + does not exists', $username->value())); + } +} diff --git a/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php b/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php new file mode 100644 index 000000000..dea981f8a --- /dev/null +++ b/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php @@ -0,0 +1,27 @@ + 'barbitas', + 'rafa' => 'pelazo', + ]; + + public function search(AuthUsername $username): ?AuthUser + { + $password = get($username->value(), self::USERS); + + return $password !== null ? new AuthUser($username, new AuthPassword($password)) : null; + } +} diff --git a/src/Backoffice/Courses/Application/BackofficeCourseResponse.php b/src/Backoffice/Courses/Application/BackofficeCourseResponse.php new file mode 100644 index 000000000..d7984b858 --- /dev/null +++ b/src/Backoffice/Courses/Application/BackofficeCourseResponse.php @@ -0,0 +1,25 @@ +id; + } + + public function name(): string + { + return $this->name; + } + + public function duration(): string + { + return $this->duration; + } +} diff --git a/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php b/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php new file mode 100644 index 000000000..489841794 --- /dev/null +++ b/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php @@ -0,0 +1,22 @@ +courses = $courses; + } + + public function courses(): array + { + return $this->courses; + } +} diff --git a/src/Backoffice/Courses/Application/Create/BackofficeCourseCreator.php b/src/Backoffice/Courses/Application/Create/BackofficeCourseCreator.php new file mode 100644 index 000000000..8493f6a46 --- /dev/null +++ b/src/Backoffice/Courses/Application/Create/BackofficeCourseCreator.php @@ -0,0 +1,18 @@ +repository->save(new BackofficeCourse($id, $name, $duration)); + } +} diff --git a/src/Backoffice/Courses/Application/Create/CreateBackofficeCourseOnCourseCreated.php b/src/Backoffice/Courses/Application/Create/CreateBackofficeCourseOnCourseCreated.php new file mode 100644 index 000000000..06ecd9541 --- /dev/null +++ b/src/Backoffice/Courses/Application/Create/CreateBackofficeCourseOnCourseCreated.php @@ -0,0 +1,23 @@ +creator->create($event->aggregateId(), $event->name(), $event->duration()); + } +} diff --git a/src/Backoffice/Courses/Application/SearchAll/AllBackofficeCoursesSearcher.php b/src/Backoffice/Courses/Application/SearchAll/AllBackofficeCoursesSearcher.php new file mode 100644 index 000000000..8cb541d64 --- /dev/null +++ b/src/Backoffice/Courses/Application/SearchAll/AllBackofficeCoursesSearcher.php @@ -0,0 +1,31 @@ +toResponse(), $this->repository->searchAll())); + } + + private function toResponse(): callable + { + return static fn (BackofficeCourse $course): BackofficeCourseResponse => new BackofficeCourseResponse( + $course->id(), + $course->name(), + $course->duration() + ); + } +} diff --git a/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQuery.php b/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQuery.php new file mode 100644 index 000000000..41df25386 --- /dev/null +++ b/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQuery.php @@ -0,0 +1,9 @@ +searcher->searchAll(); + } +} diff --git a/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php b/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php new file mode 100644 index 000000000..4ed2280a4 --- /dev/null +++ b/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php @@ -0,0 +1,36 @@ +toResponse(), $this->repository->matching($criteria))); + } + + private function toResponse(): callable + { + return static fn (BackofficeCourse $course): BackofficeCourseResponse => new BackofficeCourseResponse( + $course->id(), + $course->name(), + $course->duration() + ); + } +} diff --git a/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQuery.php b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQuery.php new file mode 100644 index 000000000..3833cd4e5 --- /dev/null +++ b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQuery.php @@ -0,0 +1,43 @@ +filters; + } + + public function orderBy(): ?string + { + return $this->orderBy; + } + + public function order(): ?string + { + return $this->order; + } + + public function limit(): ?int + { + return $this->limit; + } + + public function offset(): ?int + { + return $this->offset; + } +} diff --git a/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQueryHandler.php b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQueryHandler.php new file mode 100644 index 000000000..9be731db6 --- /dev/null +++ b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQueryHandler.php @@ -0,0 +1,23 @@ +filters()); + $order = Order::fromValues($query->orderBy(), $query->order()); + + return $this->searcher->search($filters, $order, $query->limit(), $query->offset()); + } +} diff --git a/src/Backoffice/Courses/Domain/BackofficeCourse.php b/src/Backoffice/Courses/Domain/BackofficeCourse.php new file mode 100644 index 000000000..85bc4ea53 --- /dev/null +++ b/src/Backoffice/Courses/Domain/BackofficeCourse.php @@ -0,0 +1,41 @@ + $this->id, + 'name' => $this->name, + 'duration' => $this->duration, + ]; + } + + public function id(): string + { + return $this->id; + } + + public function name(): string + { + return $this->name; + } + + public function duration(): string + { + return $this->duration; + } +} diff --git a/src/Backoffice/Courses/Domain/BackofficeCourseRepository.php b/src/Backoffice/Courses/Domain/BackofficeCourseRepository.php new file mode 100644 index 000000000..815a0a6b3 --- /dev/null +++ b/src/Backoffice/Courses/Domain/BackofficeCourseRepository.php @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php b/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php new file mode 100644 index 000000000..3a359ce41 --- /dev/null +++ b/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php @@ -0,0 +1,40 @@ +persist($course->id(), $course->toPrimitives()); + } + + public function searchAll(): array + { + return map($this->toCourse(), $this->searchAllInElastic()); + } + + public function matching(Criteria $criteria): array + { + return map($this->toCourse(), $this->searchByCriteria($criteria)); + } + + protected function aggregateName(): string + { + return 'courses'; + } + + private function toCourse(): callable + { + return static fn (array $primitives): BackofficeCourse => BackofficeCourse::fromPrimitives($primitives); + } +} diff --git a/src/Backoffice/Courses/Infrastructure/Persistence/InMemoryCacheBackofficeCourseRepository.php b/src/Backoffice/Courses/Infrastructure/Persistence/InMemoryCacheBackofficeCourseRepository.php new file mode 100644 index 000000000..d2d84c341 --- /dev/null +++ b/src/Backoffice/Courses/Infrastructure/Persistence/InMemoryCacheBackofficeCourseRepository.php @@ -0,0 +1,44 @@ +repository->save($course); + } + + public function searchAll(): array + { + return empty(self::$allCoursesCache) ? $this->searchAllAndFillCache() : self::$allCoursesCache; + } + + public function matching(Criteria $criteria): array + { + return get($criteria->serialize(), self::$matchingCache) ?: $this->searchMatchingAndFillCache($criteria); + } + + private function searchAllAndFillCache(): array + { + return self::$allCoursesCache = $this->repository->searchAll(); + } + + private function searchMatchingAndFillCache(Criteria $criteria): array + { + return self::$matchingCache[$criteria->serialize()] = $this->repository->matching($criteria); + } +} diff --git a/src/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepository.php b/src/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepository.php new file mode 100644 index 000000000..46891200b --- /dev/null +++ b/src/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepository.php @@ -0,0 +1,31 @@ +persist($course); + } + + public function searchAll(): array + { + return $this->repository(BackofficeCourse::class)->findAll(); + } + + public function matching(Criteria $criteria): array + { + $doctrineCriteria = DoctrineCriteriaConverter::convert($criteria); + + return $this->repository(BackofficeCourse::class)->matching($doctrineCriteria)->toArray(); + } +} diff --git a/src/Backoffice/README.md b/src/Backoffice/README.md deleted file mode 100644 index 91a697616..000000000 --- a/src/Backoffice/README.md +++ /dev/null @@ -1 +0,0 @@ -Here we'll have our Backoffice modules :) diff --git a/src/Backoffice/Shared/Infrastructure/Symfony/DependencyInjection/backoffice_services.yaml b/src/Backoffice/Shared/Infrastructure/Symfony/DependencyInjection/backoffice_services.yaml new file mode 100644 index 000000000..3260a112e --- /dev/null +++ b/src/Backoffice/Shared/Infrastructure/Symfony/DependencyInjection/backoffice_services.yaml @@ -0,0 +1,16 @@ +services: + # Databases + # @todo this should be from backoffice, no mooc + Doctrine\ORM\EntityManager: + factory: [ CodelyTv\Mooc\Shared\Infrastructure\Doctrine\MoocEntityManagerFactory, create ] + arguments: + - driver: '%env(MOOC_DATABASE_DRIVER)%' + host: '%env(MOOC_DATABASE_HOST)%' + port: '%env(MOOC_DATABASE_PORT)%' + dbname: '%env(MOOC_DATABASE_NAME)%' + user: '%env(MOOC_DATABASE_USER)%' + password: '%env(MOOC_DATABASE_PASSWORD)%' + - '%env(APP_ENV)%' + tags: + - { name: codely.database_connection } + public: true diff --git a/src/Mooc/Courses/Application/Create/CourseCreator.php b/src/Mooc/Courses/Application/Create/CourseCreator.php index b34526983..12b07bed4 100644 --- a/src/Mooc/Courses/Application/Create/CourseCreator.php +++ b/src/Mooc/Courses/Application/Create/CourseCreator.php @@ -1,33 +1,25 @@ repository = $repository; - $this->publisher = $publisher; - } + public function __invoke(CourseId $id, CourseName $name, CourseDuration $duration): void + { + $course = Course::create($id, $name, $duration); - public function create(CourseId $id, CourseTitle $title, CourseDescription $description): void - { - $course = Course::create($id, $title, $description); - - $this->repository->save($course); - - $this->publisher->publish(...$course->pullDomainEvents()); - } + $this->repository->save($course); + $this->bus->publish(...$course->pullDomainEvents()); + } } diff --git a/src/Mooc/Courses/Application/Create/CreateCourseCommand.php b/src/Mooc/Courses/Application/Create/CreateCourseCommand.php index a6e59b41f..e710c86a9 100644 --- a/src/Mooc/Courses/Application/Create/CreateCourseCommand.php +++ b/src/Mooc/Courses/Application/Create/CreateCourseCommand.php @@ -1,39 +1,27 @@ id = $id; - $this->title = $title; - $this->description = $description; - } - - public function id(): string - { - return $this->id; - } - - public function title(): string - { - return $this->title; - } - - public function description(): string - { - return $this->description; - } + public function __construct(private string $id, private string $name, private string $duration) {} + + public function id(): string + { + return $this->id; + } + + public function name(): string + { + return $this->name; + } + + public function duration(): string + { + return $this->duration; + } } diff --git a/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php b/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php index cb91aa2f1..e4685c5cb 100644 --- a/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php +++ b/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php @@ -1,28 +1,24 @@ creator = $creator; - } + public function __invoke(CreateCourseCommand $command): void + { + $id = new CourseId($command->id()); + $name = new CourseName($command->name()); + $duration = new CourseDuration($command->duration()); - public function __invoke(CreateCourseCommand $command): void - { - $id = new CourseId($command->id()); - $title = new CourseTitle($command->title()); - $description = new CourseDescription($command->description()); - - $this->creator->create($id, $title, $description); - } + $this->creator->__invoke($id, $name, $duration); + } } diff --git a/src/Mooc/Courses/Application/Find/CourseFinder.php b/src/Mooc/Courses/Application/Find/CourseFinder.php new file mode 100644 index 000000000..695e2aef7 --- /dev/null +++ b/src/Mooc/Courses/Application/Find/CourseFinder.php @@ -0,0 +1,26 @@ +repository->search($id); + + if ($course === null) { + throw new CourseNotExist($id); + } + + return $course; + } +} diff --git a/src/Mooc/Courses/Application/Update/CourseRenamer.php b/src/Mooc/Courses/Application/Update/CourseRenamer.php new file mode 100644 index 000000000..77c8632d4 --- /dev/null +++ b/src/Mooc/Courses/Application/Update/CourseRenamer.php @@ -0,0 +1,31 @@ +finder = new CourseFinder($repository); + } + + public function __invoke(CourseId $id, CourseName $newName): void + { + $course = $this->finder->__invoke($id); + + $course->rename($newName); + + $this->repository->save($course); + $this->bus->publish(...$course->pullDomainEvents()); + } +} diff --git a/src/Mooc/Courses/Domain/Course.php b/src/Mooc/Courses/Domain/Course.php index 4a46bbcb8..854ecdd4e 100644 --- a/src/Mooc/Courses/Domain/Course.php +++ b/src/Mooc/Courses/Domain/Course.php @@ -1,6 +1,6 @@ id = $id; - $this->title = $title; - $this->description = $description; - } - - public static function create(CourseId $id, CourseTitle $title, CourseDescription $description): Course - { - $course = new self($id, $title, $description); - - $course->record( - new CourseCreatedDomainEvent( - $id, - [ - 'title' => $title->value(), - 'description' => $description->value(), - ] - ) - ); - - return $course; - } - - public function id(): CourseId - { - return $this->id; - } - - public function title(): CourseTitle - { - return $this->title; - } - - public function description(): CourseDescription - { - return $this->description; - } + public function __construct(private readonly CourseId $id, private CourseName $name, private readonly CourseDuration $duration) {} + + public static function create(CourseId $id, CourseName $name, CourseDuration $duration): self + { + $course = new self($id, $name, $duration); + + $course->record(new CourseCreatedDomainEvent($id->value(), $name->value(), $duration->value())); + + return $course; + } + + public function id(): CourseId + { + return $this->id; + } + + public function name(): CourseName + { + return $this->name; + } + + public function duration(): CourseDuration + { + return $this->duration; + } + + public function rename(CourseName $newName): void + { + $this->name = $newName; + } } diff --git a/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php b/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php index 75ed574c1..69e89f799 100644 --- a/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php +++ b/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php @@ -1,6 +1,6 @@ ['string'], - 'description' => ['string'], - ]; - } + public function __construct( + string $id, + private readonly string $name, + private readonly string $duration, + string $eventId = null, + string $occurredOn = null + ) { + parent::__construct($id, $eventId, $occurredOn); + } + + public static function eventName(): string + { + return 'course.created'; + } + + public static function fromPrimitives( + string $aggregateId, + array $body, + string $eventId, + string $occurredOn + ): DomainEvent { + return new self($aggregateId, $body['name'], $body['duration'], $eventId, $occurredOn); + } + + public function toPrimitives(): array + { + return [ + 'name' => $this->name, + 'duration' => $this->duration, + ]; + } + + public function name(): string + { + return $this->name; + } + + public function duration(): string + { + return $this->duration; + } } diff --git a/src/Mooc/Courses/Domain/CourseTitle.php b/src/Mooc/Courses/Domain/CourseDuration.php similarity index 57% rename from src/Mooc/Courses/Domain/CourseTitle.php rename to src/Mooc/Courses/Domain/CourseDuration.php index 3bdf6b326..71a56484b 100644 --- a/src/Mooc/Courses/Domain/CourseTitle.php +++ b/src/Mooc/Courses/Domain/CourseDuration.php @@ -1,11 +1,9 @@ does not exist', $this->id->value()); + } +} diff --git a/src/Mooc/Courses/Domain/CourseRepository.php b/src/Mooc/Courses/Domain/CourseRepository.php index 885b4776e..338ad9500 100644 --- a/src/Mooc/Courses/Domain/CourseRepository.php +++ b/src/Mooc/Courses/Domain/CourseRepository.php @@ -1,10 +1,14 @@ persist($course); - } -} diff --git a/src/Mooc/Courses/Infrastructure/Cdc/DatabaseMutationToCourseCreatedDomainEvent.php b/src/Mooc/Courses/Infrastructure/Cdc/DatabaseMutationToCourseCreatedDomainEvent.php new file mode 100644 index 000000000..59f78fe54 --- /dev/null +++ b/src/Mooc/Courses/Infrastructure/Cdc/DatabaseMutationToCourseCreatedDomainEvent.php @@ -0,0 +1,40 @@ + + + + + + + + + + + diff --git a/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseDuration.orm.xml b/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseDuration.orm.xml new file mode 100644 index 000000000..19349c516 --- /dev/null +++ b/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseDuration.orm.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseIdType.php b/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseIdType.php new file mode 100644 index 000000000..389f9fb53 --- /dev/null +++ b/src/Mooc/Courses/Infrastructure/Persistence/Doctrine/CourseIdType.php @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/Mooc/Courses/Infrastructure/Persistence/DoctrineCourseRepository.php b/src/Mooc/Courses/Infrastructure/Persistence/DoctrineCourseRepository.php new file mode 100644 index 000000000..376c47039 --- /dev/null +++ b/src/Mooc/Courses/Infrastructure/Persistence/DoctrineCourseRepository.php @@ -0,0 +1,23 @@ +persist($course); + } + + public function search(CourseId $id): ?Course + { + return $this->repository(Course::class)->find($id); + } +} diff --git a/src/Mooc/Courses/Infrastructure/Persistence/FileCourseRepository.php b/src/Mooc/Courses/Infrastructure/Persistence/FileCourseRepository.php new file mode 100644 index 000000000..b6a15ecbd --- /dev/null +++ b/src/Mooc/Courses/Infrastructure/Persistence/FileCourseRepository.php @@ -0,0 +1,31 @@ +fileName($course->id()->value()), serialize($course)); + } + + public function search(CourseId $id): ?Course + { + return file_exists($this->fileName($id->value())) + ? unserialize(file_get_contents($this->fileName($id->value()))) + : null; + } + + private function fileName(string $id): string + { + return sprintf('%s.%s.repo', self::FILE_PATH, $id); + } +} diff --git a/src/Mooc/CoursesCounter/Application/Find/CoursesCounterFinder.php b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterFinder.php new file mode 100644 index 000000000..000422643 --- /dev/null +++ b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterFinder.php @@ -0,0 +1,24 @@ +repository->search(); + + if ($counter === null) { + throw new CoursesCounterNotExist(); + } + + return new CoursesCounterResponse($counter->total()->value()); + } +} diff --git a/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php new file mode 100644 index 000000000..21e8a0811 --- /dev/null +++ b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php @@ -0,0 +1,17 @@ +total; + } +} diff --git a/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php new file mode 100644 index 000000000..4be728c05 --- /dev/null +++ b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php @@ -0,0 +1,9 @@ +finder->__invoke(); + } +} diff --git a/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php b/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php new file mode 100644 index 000000000..b1131d11d --- /dev/null +++ b/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php @@ -0,0 +1,38 @@ +repository->search() ?: $this->initializeCounter(); + + if (!$counter->hasIncremented($courseId)) { + $counter->increment($courseId); + + $this->repository->save($counter); + $this->bus->publish(...$counter->pullDomainEvents()); + } + } + + private function initializeCounter(): CoursesCounter + { + return CoursesCounter::initialize(new CoursesCounterId($this->uuidGenerator->generate())); + } +} diff --git a/src/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreated.php b/src/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreated.php new file mode 100644 index 000000000..d561d7686 --- /dev/null +++ b/src/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreated.php @@ -0,0 +1,28 @@ +aggregateId()); + + apply($this->incrementer, [$courseId]); + } +} diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounter.php b/src/Mooc/CoursesCounter/Domain/CoursesCounter.php new file mode 100644 index 000000000..3d89a5da0 --- /dev/null +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounter.php @@ -0,0 +1,63 @@ +existingCourses = $existingCourses; + } + + public static function initialize(CoursesCounterId $id): self + { + return new self($id, CoursesCounterTotal::initialize()); + } + + public function id(): CoursesCounterId + { + return $this->id; + } + + public function total(): CoursesCounterTotal + { + return $this->total; + } + + public function existingCourses(): array + { + return $this->existingCourses; + } + + public function increment(CourseId $courseId): void + { + $this->total = $this->total->increment(); + $this->existingCourses[] = $courseId; + + $this->record(new CoursesCounterIncrementedDomainEvent($this->id()->value(), $this->total()->value())); + } + + public function hasIncremented(CourseId $courseId): bool + { + $existingCourse = search($this->courseIdComparator($courseId), $this->existingCourses()); + + return $existingCourse !== null; + } + + private function courseIdComparator(CourseId $courseId): callable + { + return static fn (CourseId $other): bool => $courseId->equals($other); + } +} diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterId.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterId.php new file mode 100644 index 000000000..16d0461e9 --- /dev/null +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterId.php @@ -0,0 +1,9 @@ + $this->total, + ]; + } +} diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php new file mode 100644 index 000000000..a882cdc38 --- /dev/null +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php @@ -0,0 +1,15 @@ +value() + 1); + } +} diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php new file mode 100644 index 000000000..6a2c5d95a --- /dev/null +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php @@ -0,0 +1,16 @@ + $id->value(), $value), $platform); + } + + public function convertToPHPValue($value, AbstractPlatform $platform): array + { + $scalars = parent::convertToPHPValue($value, $platform); + + return map(fn (string $value): CourseId => new CourseId($value), $scalars); + } +} diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounter.orm.xml b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounter.orm.xml new file mode 100644 index 000000000..d3d2d09c6 --- /dev/null +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounter.orm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounterTotal.orm.xml b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounterTotal.orm.xml new file mode 100644 index 000000000..ad9799fe7 --- /dev/null +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CoursesCounterTotal.orm.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php b/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php new file mode 100644 index 000000000..64622b072 --- /dev/null +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php @@ -0,0 +1,22 @@ +persist($counter); + } + + public function search(): ?CoursesCounter + { + return $this->repository(CoursesCounter::class)->findOneBy([]); + } +} diff --git a/src/Mooc/Lesson/Domain/Lesson.php b/src/Mooc/Lesson/Domain/Lesson.php deleted file mode 100644 index 0bd38fcaf..000000000 --- a/src/Mooc/Lesson/Domain/Lesson.php +++ /dev/null @@ -1,80 +0,0 @@ -id = $id; - $this->courseId = $courseId; - $this->title = $title; - $this->description = $description; - $this->estimatedDuration = $estimatedDuration; - $this->order = $order; - $this->scheduledDate = $scheduledDate; - $this->requireSubscription = $requireSubscription; - } - - public function id(): LessonId - { - return $this->id; - } - - public function courseId(): CourseId - { - return $this->courseId; - } - - public function title(): LessonTitle - { - return $this->title; - } - - public function description(): LessonDescription - { - return $this->description; - } - - public function estimatedDuration(): LessonEstimatedDuration - { - return $this->estimatedDuration; - } - - public function order(): LessonOrder - { - return $this->order; - } - - public function scheduledDate(): LessonScheduledDate - { - return $this->scheduledDate; - } - - public function requireSubscription(): bool - { - return $this->requireSubscription; - } -} diff --git a/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreated.php b/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreated.php deleted file mode 100644 index 29a483e4c..000000000 --- a/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreated.php +++ /dev/null @@ -1,34 +0,0 @@ -creator = $creator; - } - - public static function subscribedTo(): array - { - return [VideoCreatedDomainEvent::class]; - } - - public function __invoke(VideoCreatedDomainEvent $event) - { - $text = new NotificationText($event->title()); - $action = NotificationType::videoCreated(); - - apply($this->creator, [$text, $action]); - } -} diff --git a/src/Mooc/Notifications/Application/Create/NotificationCreator.php b/src/Mooc/Notifications/Application/Create/NotificationCreator.php deleted file mode 100644 index 6514fee45..000000000 --- a/src/Mooc/Notifications/Application/Create/NotificationCreator.php +++ /dev/null @@ -1,35 +0,0 @@ -uuidGenerator = $uuidGenerator; - $this->publisher = $publisher; - } - - public function __invoke(NotificationText $text, NotificationType $action) - { - $id = new NotificationId($this->uuidGenerator->next()); - - $notification = Notification::create($id, $text, $action); - - $this->publisher->publish(...$notification->pullDomainEvents()); - } -} diff --git a/src/Mooc/Notifications/Application/Send/NotificationSender.php b/src/Mooc/Notifications/Application/Send/NotificationSender.php deleted file mode 100644 index fb5cc83e8..000000000 --- a/src/Mooc/Notifications/Application/Send/NotificationSender.php +++ /dev/null @@ -1,25 +0,0 @@ -notifier = $notifier; - } - - public function __invoke(NotificationText $text, NotificationType $action) - { - $this->notifier->notify($text, $action); - // @todo Send event - } -} diff --git a/src/Mooc/Notifications/Application/Send/SendStaffNotificationOnNotificationCreated.php b/src/Mooc/Notifications/Application/Send/SendStaffNotificationOnNotificationCreated.php deleted file mode 100644 index ec8b6bedc..000000000 --- a/src/Mooc/Notifications/Application/Send/SendStaffNotificationOnNotificationCreated.php +++ /dev/null @@ -1,33 +0,0 @@ -sender = $sender; - } - - public static function subscribedTo(): array - { - return [NotificationCreatedDomainEvent::class]; - } - - public function __invoke(NotificationCreatedDomainEvent $event) - { - $text = new NotificationText($event->text()); - $type = new NotificationType($event->type()); - - $this->sender->__invoke($text, $type); - } -} diff --git a/src/Mooc/Notifications/Application/SendNewCommentReplyEmail/.gitkeep b/src/Mooc/Notifications/Application/SendNewCommentReplyEmail/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Mooc/Notifications/Application/SendNewCommentReplyPush/.gitkeep b/src/Mooc/Notifications/Application/SendNewCommentReplyPush/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Mooc/Notifications/Application/SendResetPasswordEmail/.gitkeep b/src/Mooc/Notifications/Application/SendResetPasswordEmail/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Mooc/Notifications/Domain/Notification.php b/src/Mooc/Notifications/Domain/Notification.php deleted file mode 100644 index 161ba1641..000000000 --- a/src/Mooc/Notifications/Domain/Notification.php +++ /dev/null @@ -1,69 +0,0 @@ -id = $id; - $this->text = $text; - $this->type = $type; - $this->hasBeenSent = $hasBeenSent; - } - - public static function create(NotificationId $id, NotificationText $text, NotificationType $type): Notification - { - $notification = new self($id, $text, $type, false); - - $notification->record( - new NotificationCreatedDomainEvent( - $id->value(), - [ - 'text' => $text->value(), - 'type' => $type->value(), - ] - ) - ); - - return $notification; - } - - public function id(): NotificationId - { - return $this->id; - } - - public function text(): NotificationText - { - return $this->text; - } - - public function type(): NotificationType - { - return $this->type; - } - - public function hasBeenSent(): bool - { - return $this->hasBeenSent; - } - - public function markAsNotified(): void - { - $this->hasBeenSent = true; - } -} diff --git a/src/Mooc/Notifications/Domain/NotificationCreatedDomainEvent.php b/src/Mooc/Notifications/Domain/NotificationCreatedDomainEvent.php deleted file mode 100644 index 056db69bb..000000000 --- a/src/Mooc/Notifications/Domain/NotificationCreatedDomainEvent.php +++ /dev/null @@ -1,27 +0,0 @@ - ['string'], - 'type' => ['string'], - ]; - } -} diff --git a/src/Mooc/Notifications/Domain/NotificationFinder.php b/src/Mooc/Notifications/Domain/NotificationFinder.php deleted file mode 100644 index 605cfa9ab..000000000 --- a/src/Mooc/Notifications/Domain/NotificationFinder.php +++ /dev/null @@ -1,26 +0,0 @@ -repository = $repository; - } - - public function __invoke(NotificationId $id): Notification - { - $notification = $this->repository->search($id); - - if (null === $notification) { - throw new NotificationNotFound($id); - } - - return $notification; - } -} diff --git a/src/Mooc/Notifications/Domain/NotificationId.php b/src/Mooc/Notifications/Domain/NotificationId.php deleted file mode 100644 index 60142b059..000000000 --- a/src/Mooc/Notifications/Domain/NotificationId.php +++ /dev/null @@ -1,11 +0,0 @@ -id = $id; - } - - public function errorCode(): string - { - return 'notification_not_found'; - } - - protected function errorMessage(): string - { - return sprintf('The notification <%s> has not been found', $this->id()->value()); - } - - public function id(): NotificationId - { - return $this->id; - } -} diff --git a/src/Mooc/Notifications/Domain/NotificationRepository.php b/src/Mooc/Notifications/Domain/NotificationRepository.php deleted file mode 100644 index 06fd1c998..000000000 --- a/src/Mooc/Notifications/Domain/NotificationRepository.php +++ /dev/null @@ -1,12 +0,0 @@ - is not a valid notification type', $value)); - } -} diff --git a/src/Mooc/Notifications/Domain/Notifier.php b/src/Mooc/Notifications/Domain/Notifier.php deleted file mode 100644 index 62da0d431..000000000 --- a/src/Mooc/Notifications/Domain/Notifier.php +++ /dev/null @@ -1,10 +0,0 @@ - 'New video, yeah!', - ]; - private $client; - - public function __construct(string $username, string $password) - { - $this->client = new GmailSwiftMailerEmailClient($username, $password); - } - - public function notify(NotificationText $text, NotificationType $action): void - { - $from = new EmailAddress(self::NOTIFY_FROM); - $to = new EmailAddress(self::NOTIFY_TO); - $subject = get($action->value(), self::$subjects, self::UNKNOWN_NOTIFICATION); - - $this->client->send($from, $to, $subject, $text->value()); - } -} diff --git a/src/Mooc/Notifications/Infrastructure/Notifier/FakeNotifier.php b/src/Mooc/Notifications/Infrastructure/Notifier/FakeNotifier.php deleted file mode 100644 index 8ddff734a..000000000 --- a/src/Mooc/Notifications/Infrastructure/Notifier/FakeNotifier.php +++ /dev/null @@ -1,17 +0,0 @@ -setUsername($username) - ->setPassword($password); - - $this->mailer = Swift_Mailer::newInstance($transport); - } - - public function send(EmailAddress $from, EmailAddress $to, string $subject, string $body): void - { - $message = Swift_Message::newInstance($subject) - ->setFrom($from->value()) - ->setTo($to->value()) - ->setBody($body) - ->setCharset('UTF-8'); - - $this->mailer->send($message); - } -} diff --git a/src/Mooc/Notifications/Infrastructure/Notifier/SlackNotifier.php b/src/Mooc/Notifications/Infrastructure/Notifier/SlackNotifier.php deleted file mode 100644 index 4ceb99537..000000000 --- a/src/Mooc/Notifications/Infrastructure/Notifier/SlackNotifier.php +++ /dev/null @@ -1,36 +0,0 @@ - '(q◕‿◕q)', - ]; - private $client; - - public function __construct(string $hookUrl, array $settings) - { - $this->client = new Client($hookUrl, $settings); - } - - public function notify(NotificationText $text, NotificationType $action): void - { - $message = $this->client->createMessage(); - - $message->setText( - sprintf('%s %s', get($action->value(), self::$actionsFaces, self::UNKNOWN_NOTIFICATION), $text->value()) - ); - - $this->client->sendMessage($message); - } -} diff --git a/src/Mooc/Notifications/Infrastructure/Persistence/MySqlNotificationRepository.php b/src/Mooc/Notifications/Infrastructure/Persistence/MySqlNotificationRepository.php deleted file mode 100644 index 0e72bcc10..000000000 --- a/src/Mooc/Notifications/Infrastructure/Persistence/MySqlNotificationRepository.php +++ /dev/null @@ -1,23 +0,0 @@ -repository(Notification::class)->find($id); - } - - public function save(Notification $notification): void - { - $this->persist($notification); - } -} diff --git a/src/Mooc/Shared/Domain/Courses/CourseId.php b/src/Mooc/Shared/Domain/Courses/CourseId.php index c8620b084..ff6b551a8 100644 --- a/src/Mooc/Shared/Domain/Courses/CourseId.php +++ b/src/Mooc/Shared/Domain/Courses/CourseId.php @@ -1,11 +1,9 @@ ensureIsValidUrl($value); + public function __construct(string $value) + { + $this->ensureIsValidUrl($value); - parent::__construct($value); - } + parent::__construct($value); + } - private function ensureIsValidUrl(string $url): void - { - if (false === filter_var($url, FILTER_VALIDATE_URL)) { - throw new \InvalidArgumentException(sprintf('The url <%s> is not well formatted', $url)); - } - } + private function ensureIsValidUrl(string $url): void + { + if (filter_var($url, FILTER_VALIDATE_URL) === false) { + throw new InvalidArgumentException(sprintf('The url <%s> is not well formatted', $url)); + } + } } diff --git a/src/Mooc/Shared/Infrastructure/Doctrine/DbalTypesSearcher.php b/src/Mooc/Shared/Infrastructure/Doctrine/DbalTypesSearcher.php new file mode 100644 index 000000000..28823de48 --- /dev/null +++ b/src/Mooc/Shared/Infrastructure/Doctrine/DbalTypesSearcher.php @@ -0,0 +1,69 @@ + !in_array($possibleModule, ['.', '..'], true), + scandir($path) + ); + } + + private static function possibleDbalPaths(string $path): array + { + return map( + static function (mixed $_unused, string $module) use ($path) { + $mappingsPath = self::MAPPINGS_PATH; + + return realpath("$path/$module/$mappingsPath"); + }, + array_flip(self::modulesInPath($path)) + ); + } + + private static function isExistingDbalPath(): callable + { + return static fn (string $path): bool => !empty($path); + } + + private static function dbalClassesSearcher(string $contextName): callable + { + return static function (array $totalNamespaces, string $path) use ($contextName): array { + $possibleFiles = scandir($path); + $files = filter(static fn (string $file): bool => str_ends_with($file, 'Type.php'), $possibleFiles); + + $namespaces = map( + static function (string $file) use ($path, $contextName): string { + $fullPath = "$path/$file"; + $splittedPath = explode("/src/$contextName/", $fullPath); + + $classWithoutPrefix = str_replace(['.php', '/'], ['', '\\'], $splittedPath[1]); + + return "CodelyTv\\$contextName\\$classWithoutPrefix"; + }, + $files + ); + + return array_merge($totalNamespaces, $namespaces); + }; + } +} diff --git a/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php b/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php new file mode 100644 index 000000000..85f65ed63 --- /dev/null +++ b/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php @@ -0,0 +1,52 @@ + !in_array($possibleModule, ['.', '..'], true), + scandir($path) + ); + } + + private static function possibleMappingPaths(string $path): array + { + return map( + static function (mixed $_unused, string $module) use ($path) { + $mappingsPath = self::MAPPINGS_PATH; + + return realpath("$path/$module/$mappingsPath"); + }, + array_flip(self::modulesInPath($path)) + ); + } + + private static function isExistingMappingPath(): callable + { + return static fn (string $path): bool => !empty($path); + } + + private static function namespaceFormatter(string $baseNamespace): callable + { + return static fn (string $path, string $module): string => "$baseNamespace\\$module\Domain"; + } +} diff --git a/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php b/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php index 3cfb476a0..71f0b4566 100644 --- a/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php +++ b/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php @@ -1,36 +1,33 @@ 'Shared/Infrastructure/Persistence', - - 'Videos\Domain' => 'Videos/Infrastructure/Persistence', - 'Students\Domain' => 'Students/Infrastructure/Persistence', - 'Steps\Domain' => 'Steps/Infrastructure/Persistence', - ]; - - public static function create(array $parameters, $rootPath, $onDemand, $schemaFile): EntityManagerInterface - { - return DoctrineEntityManagerFactory::create( - $parameters, - self::getNormalizedPrefixes($rootPath), - $onDemand, - $schemaFile - ); - } - - private static function getNormalizedPrefixes($rootPath) - { - return apply(new PrefixesNormalizer(realpath($rootPath), self::$namespace), [self::$prefixes]); - } + private const SCHEMA_PATH = __DIR__ . '/../../../../../etc/databases/mooc.sql'; + + public static function create(array $parameters, string $environment): EntityManagerInterface + { + $isDevMode = $environment !== 'prod'; + + $prefixes = array_merge( + DoctrinePrefixesSearcher::inPath(__DIR__ . '/../../../../Mooc', 'CodelyTv\Mooc'), + DoctrinePrefixesSearcher::inPath(__DIR__ . '/../../../../Backoffice', 'CodelyTv\Backoffice') + ); + + $dbalCustomTypesClasses = DbalTypesSearcher::inPath(__DIR__ . '/../../../../Mooc', 'Mooc'); + + return DoctrineEntityManagerFactory::create( + $parameters, + $prefixes, + $isDevMode, + self::SCHEMA_PATH, + $dbalCustomTypesClasses + ); + } } diff --git a/src/Mooc/Shared/Infrastructure/Doctrine/PrefixesNormalizer.php b/src/Mooc/Shared/Infrastructure/Doctrine/PrefixesNormalizer.php deleted file mode 100644 index f617a8283..000000000 --- a/src/Mooc/Shared/Infrastructure/Doctrine/PrefixesNormalizer.php +++ /dev/null @@ -1,28 +0,0 @@ -path = $path; - $this->namespacePrefix = $namespacePrefix ? $namespacePrefix . '\\' : ''; - } - - public function __invoke(array $prefixes) - { - $goodPrefixes = []; - - foreach ($prefixes as $className => $path) { - $goodPrefixes[sprintf('%s/%s', $this->path, $path)] = sprintf('%s%s', $this->namespacePrefix, $className); - } - - return $goodPrefixes; - } -} diff --git a/src/Mooc/Shared/Infrastructure/Persistence/Videos.VideoUrl.orm.yml b/src/Mooc/Shared/Infrastructure/Persistence/Videos.VideoUrl.orm.yml deleted file mode 100644 index 56f06c7ff..000000000 --- a/src/Mooc/Shared/Infrastructure/Persistence/Videos.VideoUrl.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Shared\Domain\Videos\VideoUrl: - type: embeddable - - fields: - value: - type: string - column: url diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/CodelyTvMoocBundle.php b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/CodelyTvMoocBundle.php deleted file mode 100644 index 4fd982de3..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/CodelyTvMoocBundle.php +++ /dev/null @@ -1,23 +0,0 @@ -addCompilerPass(new DomainEventSubscribersConfigurationCompilerPass('codely.mooc.subscriber')); - $container->addCompilerPass(new DatabasesConnectionCompilerPass('codely.mooc.database')); - $container->addCompilerPass(new TransactionalServiceCompilerPass('codely.mooc.domain_event_publisher')); - } -} diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/CodelyTvMoocExtension.php b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/CodelyTvMoocExtension.php deleted file mode 100644 index 62501f990..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/CodelyTvMoocExtension.php +++ /dev/null @@ -1,28 +0,0 @@ -registerForAutoconfiguration(CommandHandler::class)->addTag('codely.mooc.command'); - $container->registerForAutoconfiguration(QueryHandler::class)->addTag('codely.mooc.query'); - $container->registerForAutoconfiguration(DomainEventSubscriber::class)->addTag('codely.mooc.subscriber'); - - $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/Resources')); - - $loader->load('mooc_extension.yml'); - $loader->load(sprintf('mooc_config_%s.yml', $container->getParameter('kernel.environment'))); - } -} diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/_mooc_infrastructure.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/_mooc_infrastructure.yml deleted file mode 100644 index b830e5967..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/_mooc_infrastructure.yml +++ /dev/null @@ -1,3 +0,0 @@ -imports: - - { resource: mooc_arranger.yml } - - { resource: mooc_database.yml } diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_arranger.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_arranger.yml deleted file mode 100644 index 434189d32..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_arranger.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - - CodelyTv\Test\Mooc\Shared\Infrastructure\MoocEnvironmentArranger: - autowire: true - public: true diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_database.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_database.yml deleted file mode 100644 index 545aedfb3..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure/mooc_database.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - - Doctrine\ORM\EntityManager: - factory: [CodelyTv\Mooc\Shared\Infrastructure\Doctrine\MoocEntityManagerFactory, create] - arguments: - - driver: '%mooc_database_driver%' - host: '%mooc_database_host%' - port: '%mooc_database_port%' - dbname: '%mooc_database_name%' - user: '%mooc_database_user%' - password: '%mooc_database_password%' - - '%codely.mooc.root_path%' - - '%codely.mooc.infrastructure.database.on_demand%' - - '%codely.mooc.infrastructure.database.schema_file%' - tags: - - { name: codely.mooc.database } - public: true diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/modules/mooc_modules.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/modules/mooc_modules.yml deleted file mode 100644 index bb666547d..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/modules/mooc_modules.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - _defaults: - autoconfigure: true - autowire: true - - CodelyTv\Mooc\: - resource: '../../../../../../../*' - - # Notifications - CodelyTv\Mooc\Notifications\Domain\Notifier: '@CodelyTv\Mooc\Notifications\Infrastructure\Notifier\SlackNotifier' - - CodelyTv\Mooc\Notifications\Infrastructure\Notifier\SlackNotifier: - arguments: - $hookUrl: '%slack_video_notification_hook_url%' - $settings: - - username: '%slack_video_notification_username%' - channel: '%slack_video_notification_channel%' diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config.yml deleted file mode 100644 index 2bc36609f..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config.yml +++ /dev/null @@ -1,6 +0,0 @@ -parameters: - - codely.mooc.root_path: '%kernel.root_dir%/../../src/Mooc/' - - codely.mooc.infrastructure.database.on_demand: false - codely.mooc.infrastructure.database.schema_file: '%kernel.root_dir%/../../databases/mooc.sql' diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_dev.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_dev.yml deleted file mode 100644 index 3daa5bfec..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_dev.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - { resource: mooc_config.yml } - - -parameters: - codely.mooc.infrastructure.database.on_demand: true diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_prod.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_prod.yml deleted file mode 100644 index 825ac1180..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_prod.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: mooc_config.yml } diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_test.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_test.yml deleted file mode 100644 index 9afb8501c..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_config_test.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - { resource: mooc_config_dev.yml } - - -parameters: - mooc_database_name: codely_mooc_tests_%kernel.name% diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_extension.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_extension.yml deleted file mode 100644 index 94e48e21e..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_extension.yml +++ /dev/null @@ -1,5 +0,0 @@ -imports: - - { resource: mooc_parameters.yml } - - { resource: infrastructure/_mooc_infrastructure.yml } - - { resource: modules/mooc_modules.yml } - - { resource: services/_mooc_services.yml } diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_parameters.yml.dist b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_parameters.yml.dist deleted file mode 100644 index 7dcfc615e..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/mooc_parameters.yml.dist +++ /dev/null @@ -1,15 +0,0 @@ -parameters: - - mooc_database_driver: pdo_mysql - mooc_database_host: '%env(MYSQL_HOST)%' - mooc_database_port: '%env(MYSQL_PORT)%' - mooc_database_name: codely_video - mooc_database_user: root - mooc_database_password: '%env(MYSQL_ROOT_PASSWORD)%' - - video_notification_email_username: 'your@email.com' - video_notification_email_password: 'yourSecretPassword' - - slack_video_notification_hook_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX' - slack_video_notification_username: 'YourUsername' - slack_video_notification_channel: '#your_cannel' diff --git a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/services/_mooc_services.yml b/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/services/_mooc_services.yml deleted file mode 100644 index 87dc48a7c..000000000 --- a/src/Mooc/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/services/_mooc_services.yml +++ /dev/null @@ -1,22 +0,0 @@ -services: - - CodelyTv\Shared\Infrastructure\Bus\Command\SymfonySyncCommandBus: - arguments: [!tagged codely.mooc.command] - lazy: true - - CodelyTv\Shared\Infrastructure\Bus\Query\SymfonySyncQueryBus: - arguments: [!tagged codely.mooc.query] - lazy: true - - CodelyTv\Shared\Infrastructure\Bus\Event\SymfonySyncEventBus: - arguments: [!tagged codely.mooc.subscriber] - lazy: true - public: true - - CodelyTv\Shared\Infrastructure\Bus\Event\SymfonySyncDomainEventPublisher: - lazy: true - public: true - - CodelyTv\Shared\Domain\Bus\Event\DomainEventPublisher: '@CodelyTv\Shared\Infrastructure\Bus\Event\SymfonySyncDomainEventPublisher' - CodelyTv\Shared\Domain\Bus\Command\CommandBus: '@CodelyTv\Shared\Infrastructure\Bus\Command\SymfonySyncCommandBus' - CodelyTv\Shared\Domain\Bus\Query\QueryBus: '@CodelyTv\Shared\Infrastructure\Bus\Query\SymfonySyncQueryBus' diff --git a/src/Mooc/Shared/Infrastructure/Symfony/DependencyInjection/mooc_services.yaml b/src/Mooc/Shared/Infrastructure/Symfony/DependencyInjection/mooc_services.yaml new file mode 100644 index 000000000..fa4f5522b --- /dev/null +++ b/src/Mooc/Shared/Infrastructure/Symfony/DependencyInjection/mooc_services.yaml @@ -0,0 +1,19 @@ +services: + # Databases + Doctrine\ORM\EntityManager: + factory: [ CodelyTv\Mooc\Shared\Infrastructure\Doctrine\MoocEntityManagerFactory, create ] + arguments: + - driver: '%env(MOOC_DATABASE_DRIVER)%' + host: '%env(MOOC_DATABASE_HOST)%' + port: '%env(MOOC_DATABASE_PORT)%' + dbname: '%env(MOOC_DATABASE_NAME)%' + user: '%env(MOOC_DATABASE_USER)%' + password: '%env(MOOC_DATABASE_PASSWORD)%' + - '%env(APP_ENV)%' + tags: + - { name: codely.database_connection } + public: true + + + # Courses + CodelyTv\Mooc\Courses\Domain\CourseRepository: '@CodelyTv\Mooc\Courses\Infrastructure\Persistence\DoctrineCourseRepository' diff --git a/src/Mooc/Steps/Application/Create/CreateStepCommandHandler.php b/src/Mooc/Steps/Application/Create/CreateStepCommandHandler.php deleted file mode 100644 index 3e7275490..000000000 --- a/src/Mooc/Steps/Application/Create/CreateStepCommandHandler.php +++ /dev/null @@ -1,26 +0,0 @@ -creator = $creator; - } - - public function __invoke(CreateVideoCommand $command) - { - $id = new VideoId($command->id()); - - $this->creator->__invoke($id); - } -} diff --git a/src/Mooc/Steps/Application/Create/CreateVideoStepCommandHandler.php b/src/Mooc/Steps/Application/Create/CreateVideoStepCommandHandler.php new file mode 100644 index 000000000..34c301890 --- /dev/null +++ b/src/Mooc/Steps/Application/Create/CreateVideoStepCommandHandler.php @@ -0,0 +1,14 @@ +repository = $repository; - } - - public function __invoke(VideoId $id): void - { - // $this->repository->save() - } -} diff --git a/src/Mooc/Steps/Application/Create/VideoStepCreator.php b/src/Mooc/Steps/Application/Create/VideoStepCreator.php new file mode 100644 index 000000000..00a952308 --- /dev/null +++ b/src/Mooc/Steps/Application/Create/VideoStepCreator.php @@ -0,0 +1,14 @@ +statement = $statement; - } - - public function points(): StepPoints - { - return new StepPoints(5); - } - - public function statement(): ChallengeStepStatement - { - return $this->statement; - } -} diff --git a/src/Mooc/Steps/Domain/Challenge/ChallengeStepStatement.php b/src/Mooc/Steps/Domain/Challenge/ChallengeStepStatement.php deleted file mode 100644 index f2f111d8c..000000000 --- a/src/Mooc/Steps/Domain/Challenge/ChallengeStepStatement.php +++ /dev/null @@ -1,11 +0,0 @@ -questions = $questions; - } - - public function points(): StepPoints - { - return new StepPoints(10); - } - - public function questions(): array - { - return $this->questions; - } + /** @var QuizStepQuestion[] */ + private array $questions; + + public function __construct( + StepId $id, + StepTitle $title, + StepDuration $duration, + QuizStepQuestion ...$questions + ) { + parent::__construct($id, $title, $duration); + + $this->questions = $questions; + } } diff --git a/src/Mooc/Steps/Domain/Quiz/QuizStepAnswer.php b/src/Mooc/Steps/Domain/Quiz/QuizStepAnswer.php deleted file mode 100644 index bdfd5a4ce..000000000 --- a/src/Mooc/Steps/Domain/Quiz/QuizStepAnswer.php +++ /dev/null @@ -1,40 +0,0 @@ -answer = $answer; - $this->isCorrect = $isCorrect; - } - - public function toValues(): array - { - return [ - 'answer' => $this->answer(), - 'is_correct' => $this->isCorrect(), - ]; - } - - public static function fromValues(array $values): self - { - return new self($values['answer'], $values['is_correct']); - } - - public function answer(): string - { - return $this->answer; - } - - public function isCorrect(): bool - { - return $this->isCorrect; - } -} diff --git a/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php b/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php index 5541c185e..157172596 100644 --- a/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php +++ b/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php @@ -1,57 +1,22 @@ question = $question; - $this->answers = $answers; - } - - public function toValues(): array - { - return [ - 'question' => $this->question(), - 'answers' => map($this->answerToValues(), $this->answers()), - ]; - } - - public static function fromValues(array $values): self - { - return new self($values['question'], ...map(self::valuesToAnswer(), $values['answers'])); - } - - public function question(): string - { - return $this->question; - } + public function __construct(private string $question, private array $answers) {} - /** @return QuizStepAnswer[] */ - public function answers(): array - { - return $this->answers; - } + public static function fromString(string $value): self + { + [$question, $answers] = explode('----', $value); - private function answerToValues(): callable - { - return static function (QuizStepAnswer $answer): array { - return $answer->toValues(); - }; - } + return new self($question, explode('****', $answers)); + } - private static function valuesToAnswer(): callable - { - return static function (array $values): QuizStepAnswer { - return QuizStepAnswer::fromValues($values); - }; - } + public function toString(): string + { + return $this->question . '----' . implode('****', $this->answers); + } } diff --git a/src/Mooc/Steps/Domain/Step.php b/src/Mooc/Steps/Domain/Step.php index d379bdde3..b59277cde 100644 --- a/src/Mooc/Steps/Domain/Step.php +++ b/src/Mooc/Steps/Domain/Step.php @@ -1,67 +1,16 @@ id = $id; - $this->lessonId = $lessonId; - $this->title = $title; - $this->estimatedDuration = $estimatedDuration; - $this->order = $order; - $this->creationDate = $creationDate; - } - - public function id(): StepId - { - return $this->id; - } - - public function lessonId(): LessonId - { - return $this->lessonId; - } - - public function title(): StepTitle - { - return $this->title; - } - - public function estimatedDuration(): StepEstimatedDuration - { - return $this->estimatedDuration; - } - - abstract public function points(): StepPoints; - - public function order(): StepOrder - { - return $this->order; - } - - public function creationDate(): DateTimeImmutable - { - return $this->creationDate; - } + public function __construct( + public readonly StepId $id, + private readonly StepTitle $title, + private readonly StepDuration $duration + ) {} } diff --git a/src/Mooc/Steps/Domain/StepOrder.php b/src/Mooc/Steps/Domain/StepDuration.php similarity index 57% rename from src/Mooc/Steps/Domain/StepOrder.php rename to src/Mooc/Steps/Domain/StepDuration.php index 5e8aa151d..e3091e195 100644 --- a/src/Mooc/Steps/Domain/StepOrder.php +++ b/src/Mooc/Steps/Domain/StepDuration.php @@ -1,11 +1,9 @@ steps = $steps; - } - -} diff --git a/src/Mooc/Steps/Domain/Video/VideoStep.php b/src/Mooc/Steps/Domain/Video/VideoStep.php index d1a3e3d13..82c36c34e 100644 --- a/src/Mooc/Steps/Domain/Video/VideoStep.php +++ b/src/Mooc/Steps/Domain/Video/VideoStep.php @@ -1,52 +1,22 @@ videoUrl = $videoUrl; - $this->text = $text; - } - - public function points(): StepPoints - { - return new StepPoints(100); - } - - public function videoUrl(): VideoUrl - { - return $this->videoUrl; - } - - public function text(): VideoStepText - { - return $this->text; - } + public function __construct( + StepId $id, + StepTitle $title, + StepDuration $duration, + private readonly VideoStepUrl $url + ) { + parent::__construct($id, $title, $duration); + } } diff --git a/src/Mooc/Steps/Domain/Video/VideoStepText.php b/src/Mooc/Steps/Domain/Video/VideoStepUrl.php similarity index 57% rename from src/Mooc/Steps/Domain/Video/VideoStepText.php rename to src/Mooc/Steps/Domain/Video/VideoStepUrl.php index 6b6cc9f0d..17a4d81f7 100644 --- a/src/Mooc/Steps/Domain/Video/VideoStepText.php +++ b/src/Mooc/Steps/Domain/Video/VideoStepUrl.php @@ -1,11 +1,9 @@ + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Exercise.ExerciseStepContent.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Exercise.ExerciseStepContent.orm.xml new file mode 100644 index 000000000..09e493da9 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Exercise.ExerciseStepContent.orm.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Quiz.QuizStep.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Quiz.QuizStep.orm.xml new file mode 100644 index 000000000..4b116a567 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Quiz.QuizStep.orm.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/QuizStepQuestionsType.php b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/QuizStepQuestionsType.php new file mode 100644 index 000000000..677fee748 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/QuizStepQuestionsType.php @@ -0,0 +1,40 @@ + $question->toString(), $value), + $platform + ); + } + + public function convertToPHPValue($value, AbstractPlatform $platform): array + { + $scalars = parent::convertToPHPValue($value, $platform); + + return map(fn (string $value): QuizStepQuestion => QuizStepQuestion::fromString($value), $scalars); + } +} diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Step.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Step.orm.xml new file mode 100644 index 000000000..229add0ca --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Step.orm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepDuration.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepDuration.orm.xml new file mode 100644 index 000000000..c13079530 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepDuration.orm.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepIdType.php b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepIdType.php new file mode 100644 index 000000000..47b979668 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/StepIdType.php @@ -0,0 +1,16 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStep.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStep.orm.xml new file mode 100644 index 000000000..5302800f2 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStep.orm.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStepUrl.orm.xml b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStepUrl.orm.xml new file mode 100644 index 000000000..a4141ba8f --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/Doctrine/Video.VideoStepUrl.orm.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/Mooc/Steps/Infrastructure/Persistence/MySqlStepRepository.php b/src/Mooc/Steps/Infrastructure/Persistence/MySqlStepRepository.php new file mode 100644 index 000000000..45a1151d3 --- /dev/null +++ b/src/Mooc/Steps/Infrastructure/Persistence/MySqlStepRepository.php @@ -0,0 +1,28 @@ +persist($step); + } + + public function search(StepId $id): ?Step + { + return $this->repository(Step::class)->find($id); + } + + public function delete(Step $step): void + { + $this->remove($step); + } +} diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Quiz.QuizStep.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/Quiz.QuizStep.orm.yml deleted file mode 100644 index 67f719f39..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/Quiz.QuizStep.orm.yml +++ /dev/null @@ -1,8 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\Quiz\QuizStep: - type: entity - table: step_quiz - - fields: - questions: - type: quiz_step_questions - column: questions diff --git a/src/Mooc/Steps/Infrastructure/Persistence/QuizStepQuestionsType.php b/src/Mooc/Steps/Infrastructure/Persistence/QuizStepQuestionsType.php deleted file mode 100644 index de4163de4..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/QuizStepQuestionsType.php +++ /dev/null @@ -1,45 +0,0 @@ -questionFromValues(), parent::convertToPHPValue($value, $platform)); - } - - /** @var QuizStepQuestion[] $value */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - return parent::convertToDatabaseValue(map($this->questionToValues(), $value), $platform); - } - - private function questionFromValues(): callable - { - return static function (array $values): QuizStepQuestion { - return QuizStepQuestion::fromValues($values); - }; - } - - private function questionToValues(): callable - { - return static function (QuizStepQuestion $question): array { - return $question->toValues(); - }; - } -} diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Step.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/Step.orm.yml deleted file mode 100644 index f18135d9d..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/Step.orm.yml +++ /dev/null @@ -1,35 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\Step: - type: entity - inheritanceType: JOINED - discriminatorColumn: - name: type - type: smallint - discriminatorMap: - 1: CodelyTv\Mooc\Steps\Domain\Challenge\ChallengeStep - 2: CodelyTv\Mooc\Steps\Domain\Quiz\QuizStep - 3: CodelyTv\Mooc\Steps\Domain\Video\VideoStep - table: steps - - id: - id: - type: step_id - length: 36 - - fields: - lessonId: - type: lesson_id - column: lesson_id - creationDate: - type: datetime_immutable - column: creation_date - - embedded: - title: - class: CodelyTv\Mooc\Steps\Domain\StepTitle - columnPrefix: false - estimatedDuration: - class: CodelyTv\Mooc\Steps\Domain\StepEstimatedDuration - columnPrefix: false - order: - class: CodelyTv\Mooc\Steps\Domain\StepOrder - columnPrefix: false diff --git a/src/Mooc/Steps/Infrastructure/Persistence/StepEstimatedDuration.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/StepEstimatedDuration.orm.yml deleted file mode 100644 index 936b7d01c..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/StepEstimatedDuration.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\StepEstimatedDuration: - type: embeddable - - fields: - value: - type: integer - column: estimated_duration diff --git a/src/Mooc/Steps/Infrastructure/Persistence/StepIdType.php b/src/Mooc/Steps/Infrastructure/Persistence/StepIdType.php deleted file mode 100644 index e8c98d653..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/StepIdType.php +++ /dev/null @@ -1,30 +0,0 @@ -value(); - } -} diff --git a/src/Mooc/Steps/Infrastructure/Persistence/StepOrder.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/StepOrder.orm.yml deleted file mode 100644 index 38e8cbaf7..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/StepOrder.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\StepOrder: - type: embeddable - - fields: - value: - type: integer - column: step_order diff --git a/src/Mooc/Steps/Infrastructure/Persistence/StepRepositoryMySql.php b/src/Mooc/Steps/Infrastructure/Persistence/StepRepositoryMySql.php deleted file mode 100644 index aa6c307aa..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/StepRepositoryMySql.php +++ /dev/null @@ -1,17 +0,0 @@ -persist($step); - } -} diff --git a/src/Mooc/Steps/Infrastructure/Persistence/StepTitle.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/StepTitle.orm.yml deleted file mode 100644 index 80c5652d5..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/StepTitle.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\StepTitle: - type: embeddable - - fields: - value: - type: string - column: title diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStep.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStep.orm.yml deleted file mode 100644 index b731651d6..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStep.orm.yml +++ /dev/null @@ -1,11 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\Video\VideoStep: - type: entity - table: step_video - - embedded: - videoUrl: - class: CodelyTv\Mooc\Shared\Domain\Videos\VideoUrl - columnPrefix: false - text: - class: CodelyTv\Mooc\Steps\Domain\Video\VideoStepText - columnPrefix: false diff --git a/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStepText.orm.yml b/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStepText.orm.yml deleted file mode 100644 index c121e90a2..000000000 --- a/src/Mooc/Steps/Infrastructure/Persistence/Video.VideoStepText.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Steps\Domain\Video\VideoStepText: - type: embeddable - - fields: - value: - type: string - column: text diff --git a/src/Mooc/Students/Application/Find/FindStudentQuery.php b/src/Mooc/Students/Application/Find/FindStudentQuery.php deleted file mode 100644 index c72b36a65..000000000 --- a/src/Mooc/Students/Application/Find/FindStudentQuery.php +++ /dev/null @@ -1,22 +0,0 @@ -id = $id; - } - - public function id(): string - { - return $this->id; - } -} diff --git a/src/Mooc/Students/Application/Find/FindStudentQueryHandler.php b/src/Mooc/Students/Application/Find/FindStudentQueryHandler.php deleted file mode 100644 index ddb39fa52..000000000 --- a/src/Mooc/Students/Application/Find/FindStudentQueryHandler.php +++ /dev/null @@ -1,27 +0,0 @@ -finder = pipe($finder, new StudentResponseConverter()); - } - - public function __invoke(FindStudentQuery $query): StudentResponse - { - $id = new StudentId($query->id()); - - return apply($this->finder, [$id]); - } -} diff --git a/src/Mooc/Students/Application/Find/StudentFinder.php b/src/Mooc/Students/Application/Find/StudentFinder.php deleted file mode 100644 index f13e38f4e..000000000 --- a/src/Mooc/Students/Application/Find/StudentFinder.php +++ /dev/null @@ -1,31 +0,0 @@ -repository = $repository; - } - - public function __invoke(StudentId $id): Student - { - $student = $this->repository->search($id); - - if (null === $student) { - throw new StudentNotExist($id); - } - - return $student; - } -} diff --git a/src/Mooc/Students/Application/Find/StudentResponse.php b/src/Mooc/Students/Application/Find/StudentResponse.php deleted file mode 100644 index df6c1e6e2..000000000 --- a/src/Mooc/Students/Application/Find/StudentResponse.php +++ /dev/null @@ -1,36 +0,0 @@ -id = $id; - $this->name = $name; - $this->totalPendingVideos = $totalPendingVideos; - } - - public function id(): string - { - return $this->id; - } - - public function name(): string - { - return $this->name; - } - - public function totalPendingVideos(): int - { - return $this->totalPendingVideos; - } -} diff --git a/src/Mooc/Students/Application/Find/StudentResponseConverter.php b/src/Mooc/Students/Application/Find/StudentResponseConverter.php deleted file mode 100644 index 59a9f1203..000000000 --- a/src/Mooc/Students/Application/Find/StudentResponseConverter.php +++ /dev/null @@ -1,19 +0,0 @@ -id()->value(), - $student->name()->value(), - $student->totalVideosCreated()->value() - ); - } -} diff --git a/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentTotalVideosCreatedOnVideoCreated.php b/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentTotalVideosCreatedOnVideoCreated.php deleted file mode 100644 index 39ee832b1..000000000 --- a/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentTotalVideosCreatedOnVideoCreated.php +++ /dev/null @@ -1,32 +0,0 @@ -increaser = $increaser; - } - - public static function subscribedTo(): array - { - return [ScalaVideoCreatedDomainEvent::class]; - } - - public function __invoke(ScalaVideoCreatedDomainEvent $event) - { - $id = new StudentId($event->creatorId()); - - apply($this->increaser, [$id]); - } -} diff --git a/src/Mooc/Students/Application/IncreasePendingVideos/StudentTotalVideosCreatedIncreaser.php b/src/Mooc/Students/Application/IncreasePendingVideos/StudentTotalVideosCreatedIncreaser.php deleted file mode 100644 index 8ad8d01af..000000000 --- a/src/Mooc/Students/Application/IncreasePendingVideos/StudentTotalVideosCreatedIncreaser.php +++ /dev/null @@ -1,30 +0,0 @@ -finder = new StudentFinder($repository); - $this->repository = $repository; - } - - public function __invoke(StudentId $id) - { - $student = $this->finder->__invoke($id); - - $student->increaseTotalVideosCreated(); - - $this->repository->save($student); - } -} diff --git a/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEvent.php b/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEvent.php deleted file mode 100644 index 4310f6e4a..000000000 --- a/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEvent.php +++ /dev/null @@ -1,25 +0,0 @@ - ['string'], - ]; - } -} diff --git a/src/Mooc/Students/Domain/Student.php b/src/Mooc/Students/Domain/Student.php deleted file mode 100644 index 9b5a1ff14..000000000 --- a/src/Mooc/Students/Domain/Student.php +++ /dev/null @@ -1,41 +0,0 @@ -id = $id; - $this->name = $name; - $this->totalVideosCreated = $totalVideosCreated; - } - - public function id(): StudentId - { - return $this->id; - } - - public function name(): StudentName - { - return $this->name; - } - - public function totalVideosCreated(): StudentTotalVideosCreated - { - return $this->totalVideosCreated; - } - - public function increaseTotalVideosCreated(): void - { - $this->totalVideosCreated = $this->totalVideosCreated->increase(); - } -} diff --git a/src/Mooc/Students/Domain/StudentId.php b/src/Mooc/Students/Domain/StudentId.php deleted file mode 100644 index 123a2d24d..000000000 --- a/src/Mooc/Students/Domain/StudentId.php +++ /dev/null @@ -1,11 +0,0 @@ -id = $id; - - parent::__construct(); - } - - public function errorCode(): string - { - return 'student_not_exist'; - } - - protected function errorMessage(): string - { - return sprintf('The student <%s> does not exists', $this->id->value()); - } -} diff --git a/src/Mooc/Students/Domain/StudentRepository.php b/src/Mooc/Students/Domain/StudentRepository.php deleted file mode 100644 index 9eec7c5de..000000000 --- a/src/Mooc/Students/Domain/StudentRepository.php +++ /dev/null @@ -1,16 +0,0 @@ -value() + 1); - } -} diff --git a/src/Mooc/Students/Domain/Students.php b/src/Mooc/Students/Domain/Students.php deleted file mode 100644 index 279a9d9ff..000000000 --- a/src/Mooc/Students/Domain/Students.php +++ /dev/null @@ -1,28 +0,0 @@ -pendingIncreaser(), $this); - } - - private function pendingIncreaser(): callable - { - return function (Student $student): void { - $student->increaseTotalVideosCreated(); - }; - } -} diff --git a/src/Mooc/Students/Infrastructure/Persistence/Student.orm.yml b/src/Mooc/Students/Infrastructure/Persistence/Student.orm.yml deleted file mode 100644 index bf0c14ba4..000000000 --- a/src/Mooc/Students/Infrastructure/Persistence/Student.orm.yml +++ /dev/null @@ -1,17 +0,0 @@ -CodelyTv\Mooc\Students\Domain\Student: - type: entity - table: students - - id: - id: - type: student_id - column: id - length: 36 - - embedded: - name: - class: CodelyTv\Mooc\Students\Domain\StudentName - columnPrefix: false - totalVideosCreated: - class: CodelyTv\Mooc\Students\Domain\StudentTotalVideosCreated - columnPrefix: false diff --git a/src/Mooc/Students/Infrastructure/Persistence/StudentIdType.php b/src/Mooc/Students/Infrastructure/Persistence/StudentIdType.php deleted file mode 100644 index fb88fdd3c..000000000 --- a/src/Mooc/Students/Infrastructure/Persistence/StudentIdType.php +++ /dev/null @@ -1,31 +0,0 @@ -value(); - } -} - diff --git a/src/Mooc/Students/Infrastructure/Persistence/StudentName.orm.yml b/src/Mooc/Students/Infrastructure/Persistence/StudentName.orm.yml deleted file mode 100644 index 30a62029c..000000000 --- a/src/Mooc/Students/Infrastructure/Persistence/StudentName.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Students\Domain\StudentName: - type: embeddable - - fields: - value: - type: string - column: name diff --git a/src/Mooc/Students/Infrastructure/Persistence/StudentRepositoryMySql.php b/src/Mooc/Students/Infrastructure/Persistence/StudentRepositoryMySql.php deleted file mode 100644 index ed79bac53..000000000 --- a/src/Mooc/Students/Infrastructure/Persistence/StudentRepositoryMySql.php +++ /dev/null @@ -1,35 +0,0 @@ -persist($student); - } - - public function saveAll(Students $students): void - { - each($this->persister(), $students); - } - - public function search(StudentId $id): ?Student - { - return $this->repository(Student::class)->find($id); - } - - public function all(): Students - { - return new Students($this->repository(Student::class)->findAll()); - } -} diff --git a/src/Mooc/Students/Infrastructure/Persistence/StudentTotalVideosCreated.orm.yml b/src/Mooc/Students/Infrastructure/Persistence/StudentTotalVideosCreated.orm.yml deleted file mode 100644 index 89599338f..000000000 --- a/src/Mooc/Students/Infrastructure/Persistence/StudentTotalVideosCreated.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Students\Domain\StudentTotalVideosCreated: - type: embeddable - - fields: - value: - type: integer - column: total_videos_created diff --git a/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandHandler.php b/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandHandler.php deleted file mode 100644 index 0537b35bf..000000000 --- a/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -publisher = $publisher; - } - - public function __invoke(PublishVideoCommentCommand $command) - { - $id = new VideoCommentId($command->id()); - $videoId = new VideoId($command->videoId()); - $content = new VideoCommentContent($command->content()); - - $this->publisher->publish($id, $videoId, $content); - } -} diff --git a/src/Mooc/VideoComments/Application/Publish/VideoCommentPublisher.php b/src/Mooc/VideoComments/Application/Publish/VideoCommentPublisher.php deleted file mode 100644 index 36ea77dc6..000000000 --- a/src/Mooc/VideoComments/Application/Publish/VideoCommentPublisher.php +++ /dev/null @@ -1,44 +0,0 @@ -repository = $repository; - $this->bus = $bus; - $this->publisher = $publisher; - } - - public function publish(VideoCommentId $id, VideoId $videoId, VideoCommentContent $content): void - { - $this->ensureVideoExist($videoId); - - $comment = VideoComment::publish($id, $videoId, $content); - - $this->repository->save($comment); - - $this->publisher->publish(...$comment->pullDomainEvents()); - } - - private function ensureVideoExist(VideoId $id): void - { - $this->bus->ask(new FindVideoQuery($id->value())); - } -} diff --git a/src/Mooc/VideoComments/Contract/PublishVideoCommentCommand.php b/src/Mooc/VideoComments/Contract/PublishVideoCommentCommand.php deleted file mode 100644 index 7613ba7fe..000000000 --- a/src/Mooc/VideoComments/Contract/PublishVideoCommentCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -id = $id; - $this->videoId = $videoId; - $this->content = $content; - } - - public function id(): string - { - return $this->id; - } - - public function videoId(): string - { - return $this->videoId; - } - - public function content(): string - { - return $this->content; - } -} diff --git a/src/Mooc/VideoComments/Contract/VideoCommentPublishedDomainEvent.php b/src/Mooc/VideoComments/Contract/VideoCommentPublishedDomainEvent.php deleted file mode 100644 index a735778ae..000000000 --- a/src/Mooc/VideoComments/Contract/VideoCommentPublishedDomainEvent.php +++ /dev/null @@ -1,27 +0,0 @@ - ['string'], - 'content' => ['string'], - ]; - } -} diff --git a/src/Mooc/VideoComments/Domain/VideoComment.php b/src/Mooc/VideoComments/Domain/VideoComment.php deleted file mode 100644 index eae6fa90c..000000000 --- a/src/Mooc/VideoComments/Domain/VideoComment.php +++ /dev/null @@ -1,55 +0,0 @@ -id = $id; - $this->videoId = $videoId; - $this->content = $content; - } - - public static function publish(VideoCommentId $id, VideoId $videoId, VideoCommentContent $content): VideoComment - { - $comment = new self($id, $videoId, $content); - - $comment->record( - new VideoCommentPublishedDomainEvent( - $id->value(), - [ - 'videoId' => $videoId->value(), - 'content' => $content->value(), - ] - ) - ); - - return $comment; - } - - public function id(): VideoCommentId - { - return $this->id; - } - - public function videoId(): VideoId - { - return $this->videoId; - } - - public function content(): VideoCommentContent - { - return $this->content; - } -} diff --git a/src/Mooc/VideoComments/Domain/VideoCommentContent.php b/src/Mooc/VideoComments/Domain/VideoCommentContent.php deleted file mode 100644 index 5e5b755ae..000000000 --- a/src/Mooc/VideoComments/Domain/VideoCommentContent.php +++ /dev/null @@ -1,34 +0,0 @@ -ensureHasMinimumLength($value); - - $this->value = $value; - } - - public function value(): string - { - return $this->value; - } - - private function ensureHasMinimumLength(string $content): void - { - $contentLength = strlen($content); - - if ($contentLength < self::MIN_LENGTH) { - throw new \InvalidArgumentException( - sprintf('The min length for a comment is %s, %s received', self::MIN_LENGTH, $contentLength) - ); - } - } -} diff --git a/src/Mooc/VideoComments/Domain/VideoCommentId.php b/src/Mooc/VideoComments/Domain/VideoCommentId.php deleted file mode 100644 index f2d924f6f..000000000 --- a/src/Mooc/VideoComments/Domain/VideoCommentId.php +++ /dev/null @@ -1,11 +0,0 @@ -persist($comment); - } -} diff --git a/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommand.php b/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommand.php deleted file mode 100644 index 5488e51f3..000000000 --- a/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommand.php +++ /dev/null @@ -1,46 +0,0 @@ -id = $id; - $this->from = $from; - $this->to = $to; - $this->message = $message; - } - - public function id(): string - { - return $this->id; - } - - public function from(): int - { - return $this->from; - } - - public function to(): int - { - return $this->to; - } - - public function message(): string - { - return $this->message; - } -} diff --git a/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandler.php b/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandler.php deleted file mode 100644 index c045a7d2b..000000000 --- a/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -creator = $creator; - } - - public function __invoke(CreateVideoHighlightCommand $command) - { - $id = new VideoHighlightId($command->id()); - $interval = SecondsInterval::fromValues($command->from(), $command->to()); - $message = new VideoHighlightMessage($command->message()); - - $this->creator->create($id, $interval, $message); - } -} diff --git a/src/Mooc/VideoHighlights/Application/Create/VideoHighlightCreator.php b/src/Mooc/VideoHighlights/Application/Create/VideoHighlightCreator.php deleted file mode 100644 index 3fe5edaa4..000000000 --- a/src/Mooc/VideoHighlights/Application/Create/VideoHighlightCreator.php +++ /dev/null @@ -1,33 +0,0 @@ -repository = $repository; - $this->publisher = $publisher; - } - - public function create(VideoHighlightId $id, SecondsInterval $interval, VideoHighlightMessage $message): void - { - $videoHighlight = VideoHighlight::create($id, $interval, $message); - - $this->repository->save($videoHighlight); - - $this->publisher->publish(...$videoHighlight->pullDomainEvents()); - } -} diff --git a/src/Mooc/VideoHighlights/Domain/VideoHighlight.php b/src/Mooc/VideoHighlights/Domain/VideoHighlight.php deleted file mode 100644 index 77c3c9de4..000000000 --- a/src/Mooc/VideoHighlights/Domain/VideoHighlight.php +++ /dev/null @@ -1,58 +0,0 @@ -id = $id; - $this->interval = $interval; - $this->message = $message; - } - - public static function create( - VideoHighlightId $id, - SecondsInterval $interval, - VideoHighlightMessage $message - ): VideoHighlight { - $videoHighlight = new self($id, $interval, $message); - - $videoHighlight->record( - new VideoHighlightCreatedDomainEvent( - $id->value(), - [ - 'from' => $interval->from()->value(), - 'to' => $interval->to()->value(), - 'message' => $message->value(), - ] - ) - ); - - return $videoHighlight; - } - - public function id(): VideoHighlightId - { - return $this->id; - } - - public function interval(): SecondsInterval - { - return $this->interval; - } - - public function message(): VideoHighlightMessage - { - return $this->message; - } -} diff --git a/src/Mooc/VideoHighlights/Domain/VideoHighlightCreatedDomainEvent.php b/src/Mooc/VideoHighlights/Domain/VideoHighlightCreatedDomainEvent.php deleted file mode 100644 index c77119d4b..000000000 --- a/src/Mooc/VideoHighlights/Domain/VideoHighlightCreatedDomainEvent.php +++ /dev/null @@ -1,29 +0,0 @@ - ['int'], - 'to' => ['int'], - 'message' => ['string'], - ]; - } -} diff --git a/src/Mooc/VideoHighlights/Domain/VideoHighlightId.php b/src/Mooc/VideoHighlights/Domain/VideoHighlightId.php deleted file mode 100644 index a564c3e3b..000000000 --- a/src/Mooc/VideoHighlights/Domain/VideoHighlightId.php +++ /dev/null @@ -1,11 +0,0 @@ -persist($videoHighlight); - } - - public function search(VideoHighlightId $id): ?VideoHighlight - { - return $this->repository(VideoHighlight::class)->find($id); - } -} diff --git a/src/Mooc/Videos/Application/Create/CreateVideoCommand.php b/src/Mooc/Videos/Application/Create/CreateVideoCommand.php index 63dec1100..0a94120f5 100644 --- a/src/Mooc/Videos/Application/Create/CreateVideoCommand.php +++ b/src/Mooc/Videos/Application/Create/CreateVideoCommand.php @@ -1,53 +1,43 @@ id = $id; - $this->type = $type; - $this->title = $title; - $this->url = $url; - $this->courseId = $courseId; - } - - public function id(): string - { - return $this->id; - } - - public function type(): string - { - return $this->type; - } - - public function title(): string - { - return $this->title; - } - - public function url(): string - { - return $this->url; - } - - public function courseId(): string - { - return $this->courseId; - } + public function __construct( + private string $id, + private string $type, + private string $title, + private string $url, + private string $courseId + ) {} + + public function id(): string + { + return $this->id; + } + + public function type(): string + { + return $this->type; + } + + public function title(): string + { + return $this->title; + } + + public function url(): string + { + return $this->url; + } + + public function courseId(): string + { + return $this->courseId; + } } diff --git a/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php b/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php index d37b24410..4e9222d0b 100644 --- a/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php +++ b/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php @@ -1,6 +1,6 @@ creator = $creator; - } + public function __invoke(CreateVideoCommand $command): void + { + $id = new VideoId($command->id()); + $type = VideoType::from($command->type()); + $title = new VideoTitle($command->title()); + $url = new VideoUrl($command->url()); + $courseId = new CourseId($command->courseId()); - public function __invoke(CreateVideoCommand $command) - { - $id = new VideoId($command->id()); - $type = new VideoType($command->type()); - $title = new VideoTitle($command->title()); - $url = new VideoUrl($command->url()); - $courseId = new CourseId($command->courseId()); - - $this->creator->create($id, $type, $title, $url, $courseId); - } + $this->creator->create($id, $type, $title, $url, $courseId); + } } diff --git a/src/Mooc/Videos/Application/Create/VideoCreator.php b/src/Mooc/Videos/Application/Create/VideoCreator.php index cfdb6d778..a3bb4a193 100644 --- a/src/Mooc/Videos/Application/Create/VideoCreator.php +++ b/src/Mooc/Videos/Application/Create/VideoCreator.php @@ -1,6 +1,6 @@ repository = $repository; - $this->publisher = $publisher; - } + public function create(VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId): void + { + $video = Video::create($id, $type, $title, $url, $courseId); - public function create(VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId): void - { - $video = Video::create($id, $type, $title, $url, $courseId); + $this->repository->save($video); - $this->repository->save($video); - - $this->publisher->publish(...$video->pullDomainEvents()); - } + $this->bus->publish(...$video->pullDomainEvents()); + } } diff --git a/src/Mooc/Videos/Application/Find/FindVideoQuery.php b/src/Mooc/Videos/Application/Find/FindVideoQuery.php index cb7a58034..5df95d32b 100644 --- a/src/Mooc/Videos/Application/Find/FindVideoQuery.php +++ b/src/Mooc/Videos/Application/Find/FindVideoQuery.php @@ -1,22 +1,17 @@ id = $id; - } - - public function id(): string - { - return $this->id; - } + public function id(): string + { + return $this->id; + } } diff --git a/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php b/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php index e1620f189..c533ce272 100644 --- a/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php +++ b/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php @@ -1,27 +1,29 @@ responseConverter = new VideoResponseConverter(); + } - public function __construct(VideoFinder $finder) - { - $this->finder = pipe($finder, new VideoResponseConverter()); - } + public function __invoke(FindVideoQuery $query): VideoResponse + { + $id = new VideoId($query->id()); - public function __invoke(FindVideoQuery $query): VideoResponse - { - $id = new VideoId($query->id()); + $video = apply($this->finder, [$id]); - return apply($this->finder, [$id]); - } + return apply($this->responseConverter, [$video]); + } } diff --git a/src/Mooc/Videos/Application/Find/VideoFinder.php b/src/Mooc/Videos/Application/Find/VideoFinder.php index 5ffc24ae0..103ff6e75 100644 --- a/src/Mooc/Videos/Application/Find/VideoFinder.php +++ b/src/Mooc/Videos/Application/Find/VideoFinder.php @@ -1,24 +1,25 @@ finder = new DomainVideoFinder($repository); - } + public function __construct(VideoRepository $repository) + { + $this->finder = new DomainVideoFinder($repository); + } - public function __invoke(VideoId $id) - { - return $this->finder->__invoke($id); - } + public function __invoke(VideoId $id): Video + { + return $this->finder->__invoke($id); + } } diff --git a/src/Mooc/Videos/Application/Find/VideoResponse.php b/src/Mooc/Videos/Application/Find/VideoResponse.php index 0504b2fc7..fb1061754 100644 --- a/src/Mooc/Videos/Application/Find/VideoResponse.php +++ b/src/Mooc/Videos/Application/Find/VideoResponse.php @@ -1,50 +1,18 @@ id = $id; - $this->type = $type; - $this->title = $title; - $this->url = $url; - $this->courseId = $courseId; - } - - public function id(): string - { - return $this->id; - } - - public function type(): string - { - return $this->type; - } - - public function title(): string - { - return $this->title; - } - - public function url(): string - { - return $this->url; - } - - public function courseId(): string - { - return $this->courseId; - } + public function __construct( + private string $id, + private string $type, + private string $title, + private string $url, + private string $courseId + ) {} } diff --git a/src/Mooc/Videos/Application/Find/VideoResponseConverter.php b/src/Mooc/Videos/Application/Find/VideoResponseConverter.php index 997ac3c11..c766fe0cf 100644 --- a/src/Mooc/Videos/Application/Find/VideoResponseConverter.php +++ b/src/Mooc/Videos/Application/Find/VideoResponseConverter.php @@ -1,6 +1,6 @@ id()->value(), - $video->type()->value(), - $video->title()->value(), - $video->url()->value(), - $video->courseId()->value() - ); - } + public function __invoke(Video $video): VideoResponse + { + return new VideoResponse( + $video->id()->value(), + $video->type()->value, + $video->title()->value(), + $video->url()->value(), + $video->courseId()->value() + ); + } } diff --git a/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php b/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php index 058bfc311..bb2ab5038 100644 --- a/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php +++ b/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php @@ -1,39 +1,27 @@ videoId = $videoId; - $this->keepFromSecond = $keepFromSecond; - $this->keepToSecond = $keepToSecond; - } - - public function videoId(): string - { - return $this->videoId; - } - - public function keepFromSecond(): int - { - return $this->keepFromSecond; - } - - public function keepToSecond(): int - { - return $this->keepToSecond; - } + public function __construct(private string $videoId, private int $keepFromSecond, private int $keepToSecond) {} + + public function videoId(): string + { + return $this->videoId; + } + + public function keepFromSecond(): int + { + return $this->keepFromSecond; + } + + public function keepToSecond(): int + { + return $this->keepToSecond; + } } diff --git a/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php b/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php index f3a05618d..94cf1e676 100644 --- a/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php +++ b/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php @@ -1,26 +1,21 @@ trimmer = $trimmer; - } + public function __invoke(TrimVideoCommand $command): void + { + $id = new VideoId($command->videoId()); + $interval = SecondsInterval::fromValues($command->keepFromSecond(), $command->keepToSecond()); - public function __invoke(TrimVideoCommand $command) - { - $id = new VideoId($command->videoId()); - $interval = SecondsInterval::fromValues($command->keepFromSecond(), $command->keepToSecond()); - - $this->trimmer->trim($id, $interval); - } + $this->trimmer->trim($id, $interval); + } } diff --git a/src/Mooc/Videos/Application/Trim/VideoTrimmer.php b/src/Mooc/Videos/Application/Trim/VideoTrimmer.php index 567889e4a..75a6d4624 100644 --- a/src/Mooc/Videos/Application/Trim/VideoTrimmer.php +++ b/src/Mooc/Videos/Application/Trim/VideoTrimmer.php @@ -1,6 +1,6 @@ finder = new VideoFinder($repository); - $this->repository = $repository; - } + public function __construct(private VideoRepository $repository) + { + $this->finder = new VideoFinder($repository); + } - public function __invoke(VideoId $id, VideoTitle $newTitle): void - { - $video = $this->finder->__invoke($id); + public function __invoke(VideoId $id, VideoTitle $newTitle): void + { + $video = $this->finder->__invoke($id); - $video->updateTitle($newTitle); + $video->updateTitle($newTitle); - $this->repository->save($video); - } + $this->repository->save($video); + } } diff --git a/src/Mooc/Videos/Domain/Video.php b/src/Mooc/Videos/Domain/Video.php index df8751436..db7a35bdc 100644 --- a/src/Mooc/Videos/Domain/Video.php +++ b/src/Mooc/Videos/Domain/Video.php @@ -1,6 +1,6 @@ id = $id; - $this->type = $type; - $this->title = $title; - $this->url = $url; - $this->courseId = $courseId; - } + public static function create( + VideoId $id, + VideoType $type, + VideoTitle $title, + VideoUrl $url, + CourseId $courseId + ): self { + $video = new self($id, $type, $title, $url, $courseId); - public static function create( - VideoId $id, - VideoType $type, - VideoTitle $title, - VideoUrl $url, - CourseId $courseId - ): Video { - $video = new self($id, $type, $title, $url, $courseId); + $video->record( + new VideoCreatedDomainEvent($id->value(), $type->value, $title->value(), $url->value(), $courseId->value()) + ); - $video->record( - new VideoCreatedDomainEvent( - $id->value(), - [ - 'type' => $type->value(), - 'title' => $title->value(), - 'url' => $url->value(), - 'courseId' => $courseId->value(), - ] - ) - ); + return $video; + } - return $video; - } + public function updateTitle(VideoTitle $newTitle): void + { + $this->title = $newTitle; + } - public function updateTitle(VideoTitle $newTitle): void - { - $this->title = $newTitle; - } + public function id(): VideoId + { + return $this->id; + } - public function id(): VideoId - { - return $this->id; - } + public function type(): VideoType + { + return $this->type; + } - public function type(): VideoType - { - return $this->type; - } + public function title(): VideoTitle + { + return $this->title; + } - public function title(): VideoTitle - { - return $this->title; - } + public function url(): VideoUrl + { + return $this->url; + } - public function url(): VideoUrl - { - return $this->url; - } - - public function courseId(): CourseId - { - return $this->courseId; - } + public function courseId(): CourseId + { + return $this->courseId; + } } diff --git a/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php b/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php index daa003996..61cd7f8e8 100644 --- a/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php +++ b/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php @@ -1,31 +1,54 @@ ['string'], - 'title' => ['string'], - 'url' => ['string'], - 'courseId' => ['string'], - ]; - } + public static function eventName(): string + { + return 'video.created'; + } + + public static function fromPrimitives( + string $aggregateId, + array $body, + string $eventId, + string $occurredOn + ): self { + return new self( + $aggregateId, + $body['type'], + $body['title'], + $body['url'], + $body['course_id'], + $eventId, + $occurredOn + ); + } + + public function toPrimitives(): array + { + return [ + 'type' => $this->type, + 'title' => $this->title, + 'url' => $this->url, + 'course_id' => $this->courseId, + ]; + } } diff --git a/src/Mooc/Videos/Domain/VideoFinder.php b/src/Mooc/Videos/Domain/VideoFinder.php index 8cac07773..2386116ef 100644 --- a/src/Mooc/Videos/Domain/VideoFinder.php +++ b/src/Mooc/Videos/Domain/VideoFinder.php @@ -1,31 +1,21 @@ repository = $repository; - } + public function __invoke(VideoId $id): Video + { + $video = $this->repository->search($id); - public function __invoke(VideoId $id): Video - { - $video = $this->repository->search($id); + if ($video === null) { + throw new VideoNotFound($id); + } - $this->ensureVideoExists($id, $video); - - return $video; - } - - private function ensureVideoExists(VideoId $id, Video $video = null): void - { - if (null === $video) { - throw new VideoNotFound($id); - } - } + return $video; + } } diff --git a/src/Mooc/Videos/Domain/VideoId.php b/src/Mooc/Videos/Domain/VideoId.php index 7302fd779..4396d6b85 100644 --- a/src/Mooc/Videos/Domain/VideoId.php +++ b/src/Mooc/Videos/Domain/VideoId.php @@ -1,11 +1,9 @@ id = $id; - - parent::__construct(); - } - - public function errorCode(): string - { - return 'video_not_found'; - } - - protected function errorMessage(): string - { - return sprintf('The video <%s> has not been found', $this->id->value()); - } + public function __construct(private readonly VideoId $id) + { + parent::__construct(); + } + + public function errorCode(): string + { + return 'video_not_found'; + } + + protected function errorMessage(): string + { + return sprintf('The video <%s> has not been found', $this->id->value()); + } } diff --git a/src/Mooc/Videos/Domain/VideoRepository.php b/src/Mooc/Videos/Domain/VideoRepository.php index cb50b6e5c..0cd6aae41 100644 --- a/src/Mooc/Videos/Domain/VideoRepository.php +++ b/src/Mooc/Videos/Domain/VideoRepository.php @@ -1,6 +1,6 @@ value is not a valid video type', $value)); - } + case SCREENCAST = 'screencast'; + case INTERVIEW = 'interview'; } diff --git a/src/Mooc/Videos/Domain/Videos.php b/src/Mooc/Videos/Domain/Videos.php index 30c300602..9a21231d5 100644 --- a/src/Mooc/Videos/Domain/Videos.php +++ b/src/Mooc/Videos/Domain/Videos.php @@ -1,6 +1,6 @@ + + + + + + + + + + + + + diff --git a/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php new file mode 100644 index 000000000..2db8f8d8c --- /dev/null +++ b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoType.orm.xml b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoType.orm.xml new file mode 100644 index 000000000..b05a427cb --- /dev/null +++ b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoType.orm.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/Mooc/Videos/Infrastructure/Persistence/Video.orm.yml b/src/Mooc/Videos/Infrastructure/Persistence/Video.orm.yml deleted file mode 100644 index 876b9138f..000000000 --- a/src/Mooc/Videos/Infrastructure/Persistence/Video.orm.yml +++ /dev/null @@ -1,25 +0,0 @@ -CodelyTv\Mooc\Videos\Domain\Video: - type: entity - table: videos - - id: - id: - type: video_id - column: id - length: 36 - - fields: - courseId: - type: course_id - column: course_id - - embedded: - type: - class: CodelyTv\Mooc\Videos\Domain\VideoType - columnPrefix: false - title: - class: CodelyTv\Mooc\Videos\Domain\VideoTitle - columnPrefix: false - url: - class: CodelyTv\Mooc\Shared\Domain\Videos\VideoUrl - columnPrefix: false diff --git a/src/Mooc/Videos/Infrastructure/Persistence/VideoIdType.php b/src/Mooc/Videos/Infrastructure/Persistence/VideoIdType.php deleted file mode 100644 index 4f8738849..000000000 --- a/src/Mooc/Videos/Infrastructure/Persistence/VideoIdType.php +++ /dev/null @@ -1,31 +0,0 @@ -value(); - } -} - diff --git a/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php b/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php index 4299c8d2a..76e4d8a0c 100644 --- a/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php +++ b/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php @@ -1,6 +1,6 @@ 'id', - 'type' => 'type', - 'title' => 'title', - 'url' => 'url', - 'course_id' => 'courseId', - ]; - - public function save(Video $video): void - { - $this->persist($video); - } - - public function search(VideoId $id): ?Video - { - return $this->repository(Video::class)->find($id); - } - - public function searchByCriteria(Criteria $criteria): Videos - { - $doctrineCriteria = DoctrineCriteriaConverter::convert($criteria, self::$criteriaToDoctrineFields); - $videos = $this->repository(Video::class)->matching($doctrineCriteria)->toArray(); - - return new Videos($videos); - } + private static array $criteriaToDoctrineFields = [ + 'id' => 'id', + 'type' => 'type', + 'title' => 'title', + 'url' => 'url', + 'course_id' => 'courseId', + ]; + + public function save(Video $video): void + { + $this->persist($video); + } + + public function search(VideoId $id): ?Video + { + return $this->repository(Video::class)->find($id); + } + + public function searchByCriteria(Criteria $criteria): Videos + { + $doctrineCriteria = DoctrineCriteriaConverter::convert($criteria, self::$criteriaToDoctrineFields); + $videos = $this->repository(Video::class)->matching($doctrineCriteria)->toArray(); + + return new Videos($videos); + } } diff --git a/src/Mooc/Videos/Infrastructure/Persistence/VideoTitle.orm.yml b/src/Mooc/Videos/Infrastructure/Persistence/VideoTitle.orm.yml deleted file mode 100644 index 730a5c931..000000000 --- a/src/Mooc/Videos/Infrastructure/Persistence/VideoTitle.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Videos\Domain\VideoTitle: - type: embeddable - - fields: - value: - type: string - column: title diff --git a/src/Mooc/Videos/Infrastructure/Persistence/VideoType.orm.yml b/src/Mooc/Videos/Infrastructure/Persistence/VideoType.orm.yml deleted file mode 100644 index ac6da1871..000000000 --- a/src/Mooc/Videos/Infrastructure/Persistence/VideoType.orm.yml +++ /dev/null @@ -1,7 +0,0 @@ -CodelyTv\Mooc\Videos\Domain\VideoType: - type: embeddable - - fields: - value: - type: string - column: type diff --git a/src/Retention/Campaign/Application/NewCourseAvailable/Schedule/.gitkeep b/src/Retention/Campaign/Application/NewCourseAvailable/Schedule/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Campaign/Application/NewCourseAvailable/Trigger/.gitkeep b/src/Retention/Campaign/Application/NewCourseAvailable/Trigger/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Campaign/Application/WelcomeUser/Trigger/.gitkeep b/src/Retention/Campaign/Application/WelcomeUser/Trigger/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Campaign/Domain/.gitkeep b/src/Retention/Campaign/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Campaign/Infrastructure/.gitkeep b/src/Retention/Campaign/Infrastructure/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Email/Application/SendNewCourseAvailable/.gitkeep b/src/Retention/Email/Application/SendNewCourseAvailable/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Email/Application/SendWelcomeUser/.gitkeep b/src/Retention/Email/Application/SendWelcomeUser/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Email/Domain/.gitkeep b/src/Retention/Email/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Email/Infrastructure/.gitkeep b/src/Retention/Email/Infrastructure/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Push/Application/SendNewCourseAvailable/.gitkeep b/src/Retention/Push/Application/SendNewCourseAvailable/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Push/Domain/.gitkeep b/src/Retention/Push/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Push/Infrastructure/.gitkeep b/src/Retention/Push/Infrastructure/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Sms/Application/.gitkeep b/src/Retention/Sms/Application/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Sms/Domain/.gitkeep b/src/Retention/Sms/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Retention/Sms/Infrastructure/.gitkeep b/src/Retention/Sms/Infrastructure/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Shared/Domain/Aggregate/AggregateRoot.php b/src/Shared/Domain/Aggregate/AggregateRoot.php index e9cb40325..8162916cf 100644 --- a/src/Shared/Domain/Aggregate/AggregateRoot.php +++ b/src/Shared/Domain/Aggregate/AggregateRoot.php @@ -1,6 +1,6 @@ domainEvents; - $this->domainEvents = []; + final public function pullDomainEvents(): array + { + $domainEvents = $this->domainEvents; + $this->domainEvents = []; - return $domainEvents; - } + return $domainEvents; + } - final protected function record(DomainEvent $domainEvent): void - { - $this->domainEvents[] = $domainEvent; - } + final protected function record(DomainEvent $domainEvent): void + { + $this->domainEvents[] = $domainEvent; + } } diff --git a/src/Shared/Domain/Aggregate/AggregateRootCollection.php b/src/Shared/Domain/Aggregate/AggregateRootCollection.php deleted file mode 100644 index 7d0a50d28..000000000 --- a/src/Shared/Domain/Aggregate/AggregateRootCollection.php +++ /dev/null @@ -1,25 +0,0 @@ -pullItemDomainEvents(), $this, []); - } - - private function pullItemDomainEvents(): callable - { - return function (array $accumulatedEvents, AggregateRoot $aggregateRoot): array { - return array_merge($accumulatedEvents, $aggregateRoot->pullDomainEvents()); - }; - } -} diff --git a/src/Shared/Domain/Assert.php b/src/Shared/Domain/Assert.php index fa8ea01e3..d1c5f6e2d 100644 --- a/src/Shared/Domain/Assert.php +++ b/src/Shared/Domain/Assert.php @@ -1,6 +1,6 @@ is not an instance of <%s>', $class, get_class($item)) - ); - } - } - - public static function money($value): void - { - if (!self::isValidMoneyAmount($value)) { - throw new InvalidArgumentException(sprintf('The value <%s> is not a valid money amount', $value)); - } - } - - private static function isValidMoneyAmount($value): bool - { - return is_numeric($value) && $value >= 0; - } + public static function instanceOf(string $class, mixed $item): void + { + if (!$item instanceof $class) { + throw new InvalidArgumentException(sprintf('The object <%s> is not an instance of <%s>', $class, $item::class)); + } + } } diff --git a/src/Shared/Domain/Bus/Command/Command.php b/src/Shared/Domain/Bus/Command/Command.php index a307add33..dceac12c7 100644 --- a/src/Shared/Domain/Bus/Command/Command.php +++ b/src/Shared/Domain/Bus/Command/Command.php @@ -1,21 +1,7 @@ requestId(); - } - - public function messageType(): string - { - return 'command'; - } -} +interface Command {} diff --git a/src/Shared/Domain/Bus/Command/CommandBus.php b/src/Shared/Domain/Bus/Command/CommandBus.php index 91422d333..fdf5e9ef9 100644 --- a/src/Shared/Domain/Bus/Command/CommandBus.php +++ b/src/Shared/Domain/Bus/Command/CommandBus.php @@ -1,10 +1,10 @@ value(); - - parent::__construct(new Uuid($eventId)); - - $this->eventId = $eventId; - DomainEventDataValidator::isValid($data, $this->rules(), static::class); - - $this->aggregateId = $aggregateId; - $this->data = $data; - $this->occurredOn = $occurredOn ?: date_to_string(new DateTimeImmutable()); - } - - abstract protected function rules(): array; - - abstract public static function eventName(): string; - - public function messageType(): string - { - return 'domain_event'; - } - - public function eventId(): string - { - return $this->eventId; - } - - public function aggregateId(): string - { - return $this->aggregateId; - } - - public function data(): array - { - return $this->data; - } - - public function occurredOn(): string - { - return $this->occurredOn; - } - - public function __call($method, $args) - { - $attributeName = $method; - if (0 === strpos($method, 'is')) { - $attributeName = lcfirst(substr($method, 2)); - } - - if (0 === strpos($method, 'has')) { - $attributeName = lcfirst(substr($method, 3)); - } - - if (isset($this->data[$attributeName])) { - return $this->data[$attributeName]; - } - - throw new RuntimeException(sprintf('The method "%s" does not exist.', $method)); - } + private readonly string $eventId; + private readonly string $occurredOn; + + public function __construct(private readonly string $aggregateId, string $eventId = null, string $occurredOn = null) + { + $this->eventId = $eventId ?: SimpleUuid::random()->value(); + $this->occurredOn = $occurredOn ?: Utils::dateToString(new DateTimeImmutable()); + } + + abstract public static function fromPrimitives( + string $aggregateId, + array $body, + string $eventId, + string $occurredOn + ): self; + + abstract public static function eventName(): string; + + abstract public function toPrimitives(): array; + + final public function aggregateId(): string + { + return $this->aggregateId; + } + + final public function eventId(): string + { + return $this->eventId; + } + + final public function occurredOn(): string + { + return $this->occurredOn; + } } diff --git a/src/Shared/Domain/Bus/Event/DomainEventPublisher.php b/src/Shared/Domain/Bus/Event/DomainEventPublisher.php deleted file mode 100644 index 8dba0c9a1..000000000 --- a/src/Shared/Domain/Bus/Event/DomainEventPublisher.php +++ /dev/null @@ -1,23 +0,0 @@ -messageId = $messageId; - } - - public function messageId(): Uuid - { - return $this->messageId; - } - - abstract public function messageType(): string; -} diff --git a/src/Shared/Domain/Bus/MessageSerializer.php b/src/Shared/Domain/Bus/MessageSerializer.php deleted file mode 100644 index f787e05d3..000000000 --- a/src/Shared/Domain/Bus/MessageSerializer.php +++ /dev/null @@ -1,50 +0,0 @@ -getMethods(); - - $methodNames = reindex($this->methodNameExtractor(), $methods); - - return map($this->nameExtractor($message), filter(not($this->isConstruct()), $methodNames)); - } - - private function methodNameExtractor(): callable - { - return function (ReflectionMethod $method): string { - return camel_to_snake($method->getName()); - }; - } - - private function nameExtractor(Request $message): callable - { - return function ($unused, $name) use ($message): string { - $methodName = snake_to_camel($name); - - return $message->$methodName(); - }; - } - - private function isConstruct(): callable - { - return function ($unused, $name) { - return $name === '__construct'; - }; - } -} diff --git a/src/Shared/Domain/Bus/Query/Asker.php b/src/Shared/Domain/Bus/Query/Asker.php deleted file mode 100644 index 98fef4391..000000000 --- a/src/Shared/Domain/Bus/Query/Asker.php +++ /dev/null @@ -1,20 +0,0 @@ -queryBus = $queryBus; - } - - public function __invoke(Query $query): ?Response - { - return $this->queryBus->ask($query); - } -} diff --git a/src/Shared/Domain/Bus/Query/Query.php b/src/Shared/Domain/Bus/Query/Query.php index b7acefb96..3ed4d1467 100644 --- a/src/Shared/Domain/Bus/Query/Query.php +++ b/src/Shared/Domain/Bus/Query/Query.php @@ -1,12 +1,7 @@ messageId(); - } -} diff --git a/src/Shared/Domain/Collection.php b/src/Shared/Domain/Collection.php index eb206bc6a..c0b664aa8 100644 --- a/src/Shared/Domain/Collection.php +++ b/src/Shared/Domain/Collection.php @@ -1,45 +1,36 @@ */ abstract class Collection implements Countable, IteratorAggregate { - /** @var array */ - private $items; - - public function __construct(array $items) - { - Assert::arrayOf($this->type(), $items); - - $this->items = $items; - } - - abstract protected function type(): string; - - public function getIterator() - { - return new ArrayIterator($this->items()); - } - - public function count() - { - return count($this->items()); - } - - protected function each(callable $fn) - { - each($fn, $this->items()); - } - - protected function items() - { - return $this->items; - } + public function __construct(private readonly array $items) + { + Assert::arrayOf($this->type(), $items); + } + + abstract protected function type(): string; + + final public function getIterator(): Traversable + { + return new ArrayIterator($this->items()); + } + + final public function count(): int + { + return count($this->items()); + } + + protected function items(): array + { + return $this->items; + } } diff --git a/src/Shared/Domain/Criteria/Criteria.php b/src/Shared/Domain/Criteria/Criteria.php index f0ff01931..876c9f441 100644 --- a/src/Shared/Domain/Criteria/Criteria.php +++ b/src/Shared/Domain/Criteria/Criteria.php @@ -1,56 +1,61 @@ filters = $filters; - $this->order = $order; - $this->offset = $offset; - $this->limit = $limit; - } - - public function hasFilters(): bool - { - return $this->filters->count() > 0; - } - - public function hasOrder(): bool - { - return null !== $this->order; - } - - public function plainFilters(): array - { - return $this->filters->filters(); - } - - public function filters(): Filters - { - return $this->filters; - } - - public function order(): ?Order - { - return $this->order; - } - - public function offset(): ?int - { - return $this->offset; - } - - public function limit(): ?int - { - return $this->limit; - } + public function __construct( + private Filters $filters, + private Order $order, + private ?int $offset, + private ?int $limit + ) {} + + public function hasFilters(): bool + { + return $this->filters->count() > 0; + } + + public function hasOrder(): bool + { + return !$this->order->isNone(); + } + + public function plainFilters(): array + { + return $this->filters->filters(); + } + + public function filters(): Filters + { + return $this->filters; + } + + public function order(): Order + { + return $this->order; + } + + public function offset(): ?int + { + return $this->offset; + } + + public function limit(): ?int + { + return $this->limit; + } + + public function serialize(): string + { + return sprintf( + '%s~~%s~~%s~~%s', + $this->filters->serialize(), + $this->order->serialize(), + $this->offset ?? 'none', + $this->limit ?? 'none' + ); + } } diff --git a/src/Shared/Domain/Criteria/Filter.php b/src/Shared/Domain/Criteria/Filter.php index d7fac8013..33f15c365 100644 --- a/src/Shared/Domain/Criteria/Filter.php +++ b/src/Shared/Domain/Criteria/Filter.php @@ -1,43 +1,43 @@ field = $field; - $this->operator = $operator; - $this->value = $value; - } - - public static function fromValues(array $values): self - { - return new self( - new FilterField($values['field']), - new FilterOperator($values['operator']), - new FilterValue($values['value']) - ); - } - - public function field(): FilterField - { - return $this->field; - } - - public function operator(): FilterOperator - { - return $this->operator; - } - - public function value(): FilterValue - { - return $this->value; - } + public function __construct( + private FilterField $field, + private FilterOperator $operator, + private FilterValue $value + ) {} + + public static function fromValues(array $values): self + { + return new self( + new FilterField($values['field']), + FilterOperator::from($values['operator']), + new FilterValue($values['value']) + ); + } + + public function field(): FilterField + { + return $this->field; + } + + public function operator(): FilterOperator + { + return $this->operator; + } + + public function value(): FilterValue + { + return $this->value; + } + + public function serialize(): string + { + return sprintf('%s.%s.%s', $this->field->value(), $this->operator->value, $this->value->value()); + } } diff --git a/src/Shared/Domain/Criteria/FilterField.php b/src/Shared/Domain/Criteria/FilterField.php index c53383e4d..fcc11e2de 100644 --- a/src/Shared/Domain/Criteria/FilterField.php +++ b/src/Shared/Domain/Criteria/FilterField.php @@ -1,11 +1,9 @@ '; - public const LT = '<'; - public const CONTAINS = 'CONTAINS'; - - public static function equal(): self - { - return new self('='); - } + case EQUAL = '='; + case NOT_EQUAL = '!='; + case GT = '>'; + case LT = '<'; + case CONTAINS = 'CONTAINS'; + case NOT_CONTAINS = 'NOT_CONTAINS'; - protected function throwExceptionForInvalidValue($value): void - { - throw new InvalidArgumentException(sprintf('The filter <%s> is invalid', $value)); - } + public function isContaining(): bool + { + return in_array($this->value, [self::CONTAINS->value, self::NOT_CONTAINS->value], true); + } } diff --git a/src/Shared/Domain/Criteria/FilterValue.php b/src/Shared/Domain/Criteria/FilterValue.php index 78aef7bd6..5576c10fb 100644 --- a/src/Shared/Domain/Criteria/FilterValue.php +++ b/src/Shared/Domain/Criteria/FilterValue.php @@ -1,11 +1,9 @@ items(), [$filter])); - } - - public function filters(): array - { - return $this->items(); - } - - private static function filterBuilder(): callable - { - return function (array $values) { - return Filter::fromValues($values); - }; - } + public static function fromValues(array $values): self + { + return new self(array_map(self::filterBuilder(), $values)); + } + + private static function filterBuilder(): callable + { + return fn (array $values): Filter => Filter::fromValues($values); + } + + public function add(Filter $filter): self + { + return new self(array_merge($this->items(), [$filter])); + } + + public function filters(): array + { + return $this->items(); + } + + public function serialize(): string + { + return reduce( + static fn (string $accumulate, Filter $filter): string => sprintf('%s^%s', $accumulate, $filter->serialize()), + $this->items(), + '' + ); + } + + protected function type(): string + { + return Filter::class; + } } diff --git a/src/Shared/Domain/Criteria/Order.php b/src/Shared/Domain/Criteria/Order.php index d5f1b90f0..dfbc8e016 100644 --- a/src/Shared/Domain/Criteria/Order.php +++ b/src/Shared/Domain/Criteria/Order.php @@ -1,32 +1,48 @@ orderBy = $orderBy; - $this->orderType = $orderType ?: OrderType::asc(); - } - - public static function createDesc(OrderBy $orderBy): Order - { - return new self($orderBy, OrderType::desc()); - } - - public function orderBy(): OrderBy - { - return $this->orderBy; - } - - public function orderType(): OrderType - { - return $this->orderType; - } + public function __construct(private OrderBy $orderBy, private OrderType $orderType) {} + + public static function createDesc(OrderBy $orderBy): self + { + return new self($orderBy, OrderType::DESC); + } + + public static function fromValues(?string $orderBy, ?string $order): self + { + return ($orderBy === null || $order === null) ? self::none() : new self( + new OrderBy($orderBy), + OrderType::from($order) + ); + } + + public static function none(): self + { + return new self(new OrderBy(''), OrderType::NONE); + } + + public function orderBy(): OrderBy + { + return $this->orderBy; + } + + public function orderType(): OrderType + { + return $this->orderType; + } + + public function isNone(): bool + { + return $this->orderType()->isNone(); + } + + public function serialize(): string + { + return sprintf('%s.%s', $this->orderBy->value(), $this->orderType->value); + } } diff --git a/src/Shared/Domain/Criteria/OrderBy.php b/src/Shared/Domain/Criteria/OrderBy.php index ac0f1a3bf..d2d054cae 100644 --- a/src/Shared/Domain/Criteria/OrderBy.php +++ b/src/Shared/Domain/Criteria/OrderBy.php @@ -1,11 +1,9 @@ value === self::NONE->value; + } } diff --git a/src/Shared/Domain/DomainError.php b/src/Shared/Domain/DomainError.php index 8b9f9c755..18eec3f93 100644 --- a/src/Shared/Domain/DomainError.php +++ b/src/Shared/Domain/DomainError.php @@ -1,6 +1,6 @@ errorMessage()); - } + public function __construct() + { + parent::__construct($this->errorMessage()); + } - abstract public function errorCode(): string; + abstract public function errorCode(): string; - abstract protected function errorMessage(): string; + abstract protected function errorMessage(): string; } diff --git a/src/Shared/Domain/EmailAddress.php b/src/Shared/Domain/EmailAddress.php deleted file mode 100644 index d221ebb9a..000000000 --- a/src/Shared/Domain/EmailAddress.php +++ /dev/null @@ -1,31 +0,0 @@ -ensureIsValidEmail($value); - - $this->value = $value; - } - - private function ensureIsValidEmail(string $value): void - { - if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { - throw new InvalidArgumentException(sprintf('The email <%s> is not valid', $value)); - } - } - - public function value(): string - { - return $this->value; - } -} diff --git a/src/Shared/Domain/InvalidUuid.php b/src/Shared/Domain/InvalidUuid.php deleted file mode 100644 index ce99bd5b1..000000000 --- a/src/Shared/Domain/InvalidUuid.php +++ /dev/null @@ -1,27 +0,0 @@ -uuid = $uuid; - } - - public function errorCode(): string - { - return 'invalid_uuid'; - } - - protected function errorMessage(): string - { - return sprintf('The <%s> is an invalid uuid', $this->uuid); - } -} diff --git a/src/Shared/Domain/Logger.php b/src/Shared/Domain/Logger.php new file mode 100644 index 000000000..fbaa7710c --- /dev/null +++ b/src/Shared/Domain/Logger.php @@ -0,0 +1,14 @@ +ensureIntervalEndsAfterStart($from, $to); - - $this->from = $from; - $this->to = $to; - } - - public function from(): Second - { - return $this->from; - } - - public function to(): Second - { - return $this->to; - } - - public static function fromValues(int $from, int $to): SecondsInterval - { - return new self(new Second($from), new Second($to)); - } - - private function ensureIntervalEndsAfterStart(Second $from, Second $to): void - { - if ($from->isBiggerThan($to)) { - throw new DomainException('To is bigger than from'); - } - } + public function __construct(private Second $from, private Second $to) + { + $this->ensureIntervalEndsAfterStart($from, $to); + } + + public static function fromValues(int $from, int $to): self + { + return new self(new Second($from), new Second($to)); + } + + private function ensureIntervalEndsAfterStart(Second $from, Second $to): void + { + if ($from->isBiggerThan($to)) { + throw new DomainException('To is bigger than from'); + } + } } diff --git a/src/Shared/Domain/Utils.php b/src/Shared/Domain/Utils.php new file mode 100644 index 000000000..92a9d1117 --- /dev/null +++ b/src/Shared/Domain/Utils.php @@ -0,0 +1,73 @@ +format(DateTimeInterface::ATOM); + } + + public static function stringToDate(string $date): DateTimeImmutable + { + return new DateTimeImmutable($date); + } + + public static function jsonEncode(array $values): string + { + return json_encode($values, JSON_THROW_ON_ERROR); + } + + public static function jsonDecode(string $json): array + { + return json_decode($json, true, flags: JSON_THROW_ON_ERROR); + } + + public static function toSnakeCase(string $text): string + { + return ctype_lower($text) ? $text : strtolower((string) preg_replace('/([^A-Z\s])([A-Z])/', '$1_$2', $text)); + } + + public static function toCamelCase(string $text): string + { + return lcfirst(str_replace('_', '', ucwords($text, '_'))); + } + + public static function dot(array $array, string $prepend = ''): array + { + $results = []; + foreach ($array as $key => $value) { + if (is_array($value) && !empty($value)) { + $results = array_merge($results, self::dot($value, $prepend . $key . '.')); + } else { + $results[$prepend . $key] = $value; + } + } + + return $results; + } + + public static function filesIn(string $path, string $fileType): array + { + return filter( + static fn (string $possibleModule): false | string => strstr($possibleModule, $fileType), + scandir($path) + ); + } + + public static function iterableToArray(iterable $iterable): array + { + if (is_array($iterable)) { + return $iterable; + } + + return iterator_to_array($iterable); + } +} diff --git a/src/Shared/Domain/UuidGenerator.php b/src/Shared/Domain/UuidGenerator.php new file mode 100644 index 000000000..1064a6edc --- /dev/null +++ b/src/Shared/Domain/UuidGenerator.php @@ -0,0 +1,10 @@ + self::stringValidator(), - 'int' => self::intValidator(), - 'float' => self::floatValidator(), - 'array' => self::arrayValidator(), - ]; - - return $validators[$type]; - } - - private static function stringValidator(): callable - { - return function ($value): bool { - return is_string($value); - }; - } - - private static function intValidator(): callable - { - return function ($value): bool { - return is_int($value); - }; - } - - private static function arrayValidator(): callable - { - return function ($value): bool { - return is_array($value); - }; - } - - private static function floatValidator(): callable - { - return function ($value): bool { - return is_float($value); - }; - } -} diff --git a/src/Shared/Domain/ValueObject/Enum.php b/src/Shared/Domain/ValueObject/Enum.php deleted file mode 100644 index bcb94447b..000000000 --- a/src/Shared/Domain/ValueObject/Enum.php +++ /dev/null @@ -1,85 +0,0 @@ -ensureIsBetweenAcceptedValues($value); - - $this->value = $value; - } - - abstract protected function throwExceptionForInvalidValue($value); - - public static function __callStatic(string $name, $args) - { - return new static(self::values()[$name]); - } - - public static function fromString(string $value) - { - return new static($value); - } - - public static function values(): array - { - $class = static::class; - - if (!isset(self::$cache[$class])) { - $reflected = new ReflectionClass($class); - self::$cache[$class] = reindex(self::keysFormatter(), $reflected->getConstants()); - } - - return self::$cache[$class]; - } - - public static function randomValue() - { - return self::values()[array_rand(self::values())]; - } - - public function value() - { - return $this->value; - } - - public function equals(Enum $other): bool - { - return $other == $this; - } - - private function ensureIsBetweenAcceptedValues($value): void - { - if (!\in_array($value, static::values(), true)) { - $this->throwExceptionForInvalidValue($value); - } - } - - public static function random(): self - { - return new static(self::randomValue()); - } - - private static function keysFormatter(): callable - { - return function ($unused, string $key): string { - return snake_to_camel(strtolower($key)); - }; - } - - public function __toString(): string - { - return (string) $this->value(); - } -} diff --git a/src/Shared/Domain/ValueObject/IntValueObject.php b/src/Shared/Domain/ValueObject/IntValueObject.php index 2a4c49582..77e13cd76 100644 --- a/src/Shared/Domain/ValueObject/IntValueObject.php +++ b/src/Shared/Domain/ValueObject/IntValueObject.php @@ -1,30 +1,20 @@ value = $value; - } + final public function value(): int + { + return $this->value; + } - public function value(): int - { - return $this->value; - } - - public function equalsTo(IntValueObject $other): bool - { - return $this->value() === $other->value(); - } - - public function isBiggerThan(IntValueObject $other): bool - { - return $this->value() > $other->value(); - } + final public function isBiggerThan(self $other): bool + { + return $this->value() > $other->value(); + } } diff --git a/src/Shared/Domain/ValueObject/SimpleUuid.php b/src/Shared/Domain/ValueObject/SimpleUuid.php new file mode 100644 index 000000000..250f41c40 --- /dev/null +++ b/src/Shared/Domain/ValueObject/SimpleUuid.php @@ -0,0 +1,7 @@ +value = $value; - } - - public function value(): string - { - return $this->value; - } - - public function __toString() - { - return $this->value(); - } + final public function value(): string + { + return $this->value; + } } diff --git a/src/Shared/Domain/ValueObject/Uuid.php b/src/Shared/Domain/ValueObject/Uuid.php index 1108ae90c..63a30fcc8 100644 --- a/src/Shared/Domain/ValueObject/Uuid.php +++ b/src/Shared/Domain/ValueObject/Uuid.php @@ -1,44 +1,44 @@ ensureIsValidUuid($value); - - $this->value = $value; - } - - public static function random(): self - { - return new self(RamseyUuid::uuid4()->toString()); - } - - public function value(): string - { - return $this->value; - } - - private function ensureIsValidUuid($id): void - { - if (!RamseyUuid::isValid($id)) { - throw new InvalidArgumentException( - sprintf('<%s> does not allow the value <%s>.', static::class, is_scalar($id) ? $id : gettype($id)) - ); - } - } - - public function __toString() - { - return $this->value(); - } + final public function __construct(protected string $value) + { + $this->ensureIsValidUuid($value); + } + + final public static function random(): self + { + return new static(RamseyUuid::uuid4()->toString()); + } + + final public function value(): string + { + return $this->value; + } + + final public function equals(self $other): bool + { + return $this->value() === $other->value(); + } + + public function __toString(): string + { + return $this->value(); + } + + private function ensureIsValidUuid(string $id): void + { + if (!RamseyUuid::isValid($id)) { + throw new InvalidArgumentException(sprintf('<%s> does not allow the value <%s>.', self::class, $id)); + } + } } diff --git a/src/Shared/Infrastructure/Api/Controller/ApiController.php b/src/Shared/Infrastructure/Api/Controller/ApiController.php deleted file mode 100644 index 672110abf..000000000 --- a/src/Shared/Infrastructure/Api/Controller/ApiController.php +++ /dev/null @@ -1,51 +0,0 @@ -queryBus = $queryBus; - $this->commandBus = $commandBus; - $this->exceptionHandler = $exceptionHandler; - - each($this->exceptionRegistrar(), $this->exceptions()); - } - - abstract protected function exceptions(): array; - - protected function dispatch(Command $command): void - { - $this->commandBus->dispatch($command); - } - - protected function ask(Query $query): ?Response - { - return $this->queryBus->ask($query); - } - - private function exceptionRegistrar(): callable - { - return function ($httpCode, $exception): void { - $this->exceptionHandler->register($exception, $httpCode); - }; - } -} diff --git a/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionLoggerSubscriber.php b/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionLoggerSubscriber.php deleted file mode 100644 index f4df7be9a..000000000 --- a/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionLoggerSubscriber.php +++ /dev/null @@ -1,70 +0,0 @@ -logger = $logger; - $this->exceptionHandler = $exceptionHandler; - } - - public static function getSubscribedEvents(): array - { - return [KernelEvents::EXCEPTION => ['onKernelException', 1]]; - } - - public function onKernelException(GetResponseForExceptionEvent $event): void - { - $exception = $event->getException(); - - $this->logException( - $exception, - sprintf( - 'Uncaught PHP Exception %s: "%s" at %s line %s', - get_class($exception), - $exception->getMessage(), - $exception->getFile(), - $exception->getLine() - ) - ); - } - - protected function logException(Exception $exception, $message): void - { - $level = $this->logLevel($this->exceptionStatusCode($exception)); - - $this->logger->log($level, $message, ['exception' => $exception]); - } - - protected function exceptionStatusCode(Exception $exception) - { - $statusCode = 500; - $exceptionClass = get_class($exception); - - if ($this->exceptionHandler->exists($exceptionClass)) { - $statusCode = $this->exceptionHandler->getStatusCode($exceptionClass); - } - - return $statusCode; - } - - private function logLevel($statusCode): string - { - return $statusCode >= 500 ? LogLevel::CRITICAL : LogLevel::ERROR; - } -} diff --git a/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionSubscriber.php b/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionSubscriber.php deleted file mode 100644 index 1ecc1ae32..000000000 --- a/src/Shared/Infrastructure/Api/EventSubscriber/ApiExceptionSubscriber.php +++ /dev/null @@ -1,67 +0,0 @@ -viewHandler = $viewHandler; - $this->exceptionHandler = $exceptionHandler; - } - - public static function getSubscribedEvents(): array - { - return [KernelEvents::EXCEPTION => ['onKernelException', 0]]; - } - - public function onKernelException(GetResponseForExceptionEvent $event): void - { - $exception = $event->getException(); - $exceptionClass = get_class($exception); - - if ($this->exceptionHandler->exists($exceptionClass)) { - $event->setResponse($this->createResponseFromApiErrorException($exception)); - } - } - - private function createResponseFromApiErrorException(Exception $exception): Response - { - $data = [ - 'code' => $this->getExceptionCode($exception), - 'message' => $exception->getMessage(), - ]; - - return $this->viewHandler->handle( - View::create($data, $this->exceptionHandler->getStatusCode(get_class($exception))) - ); - } - - private function getExceptionCode(Exception $exception) - { - $moduleExceptionClass = DomainError::class; - - return $exception instanceof $moduleExceptionClass ? $this->domainErrorCode($exception) : $exception->getCode(); - } - - private function domainErrorCode($error): string - { - /** @var DomainError $error */ - return $error->errorCode(); - } -} diff --git a/src/Shared/Infrastructure/Api/EventSubscriber/ApiResponseViewSubscriber.php b/src/Shared/Infrastructure/Api/EventSubscriber/ApiResponseViewSubscriber.php deleted file mode 100644 index 1765d8067..000000000 --- a/src/Shared/Infrastructure/Api/EventSubscriber/ApiResponseViewSubscriber.php +++ /dev/null @@ -1,28 +0,0 @@ - ['onKernelView', 200]]; - } - - public function onKernelView(GetResponseForControllerResultEvent $event): void - { - $result = $event->getControllerResult(); - - if ($result instanceof ApiHttpResponse) { - $event->setControllerResult(new View($result->data(), $result->statusCode(), $result->headers())); - } - } -} diff --git a/src/Shared/Infrastructure/Api/Exception/ApiExceptionsHttpStatusCodeMapping.php b/src/Shared/Infrastructure/Api/Exception/ApiExceptionsHttpStatusCodeMapping.php deleted file mode 100644 index 36b165579..000000000 --- a/src/Shared/Infrastructure/Api/Exception/ApiExceptionsHttpStatusCodeMapping.php +++ /dev/null @@ -1,30 +0,0 @@ - Response::HTTP_BAD_REQUEST, - ]; - - public function register($exceptionClass, $statusCode): void - { - $this->exceptions[$exceptionClass] = $statusCode; - } - - public function exists($exceptionClass): bool - { - return array_key_exists($exceptionClass, $this->exceptions); - } - - public function getStatusCode($exceptionClass) - { - return $this->exceptions[$exceptionClass]; - } -} diff --git a/src/Shared/Infrastructure/Api/Response/ApiHttpAcceptedResponse.php b/src/Shared/Infrastructure/Api/Response/ApiHttpAcceptedResponse.php deleted file mode 100644 index 1639cc49d..000000000 --- a/src/Shared/Infrastructure/Api/Response/ApiHttpAcceptedResponse.php +++ /dev/null @@ -1,20 +0,0 @@ - sprintf('%s/status/%s', $currentUrl, $requestId->value())], $headers) - ); - } -} diff --git a/src/Shared/Infrastructure/Api/Response/ApiHttpCreatedResponse.php b/src/Shared/Infrastructure/Api/Response/ApiHttpCreatedResponse.php deleted file mode 100644 index 3af611332..000000000 --- a/src/Shared/Infrastructure/Api/Response/ApiHttpCreatedResponse.php +++ /dev/null @@ -1,15 +0,0 @@ -data = $data; - $this->statusCode = $statusCode; - $this->headers = $headers; - } - - public function data(): ?array - { - return $this->data; - } - - public function statusCode(): int - { - return $this->statusCode; - } - - public function headers(): array - { - return $this->headers; - } -} diff --git a/src/Shared/Infrastructure/Api/Serializer/ApiSerializerDriver.php b/src/Shared/Infrastructure/Api/Serializer/ApiSerializerDriver.php deleted file mode 100644 index afe143ca9..000000000 --- a/src/Shared/Infrastructure/Api/Serializer/ApiSerializerDriver.php +++ /dev/null @@ -1,35 +0,0 @@ -addResourceFile(__FILE__); - } - - public function getMetadata(): array - { - return [ - StudentResponse::class => [ - 'id' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'totalPendingVideos' => ['type' => 'string'], - ], - VideoResponse::class => [ - 'id' => ['type' => 'string'], - 'type' => ['type' => 'string'], - 'title' => ['type' => 'string'], - 'url' => ['type' => 'string'], - 'courseId' => ['type' => 'string'], - ], - ]; - } -} diff --git a/src/Shared/Infrastructure/Api/Serializer/DateTimeHandler.php b/src/Shared/Infrastructure/Api/Serializer/DateTimeHandler.php deleted file mode 100644 index c4d3c3b9a..000000000 --- a/src/Shared/Infrastructure/Api/Serializer/DateTimeHandler.php +++ /dev/null @@ -1,157 +0,0 @@ -defaultFormat = $defaultFormat; - $this->defaultTimezone = new DateTimeZone($defaultTimezone); - $this->xmlCData = $xmlCData; - } - - public static function getSubscribingMethods(): array - { - $methods = []; - $types = ['DateTime', 'DateInterval']; - - foreach (['json', 'xml', 'yml'] as $format) { - $methods[] = [ - 'type' => 'DateTime', - 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION, - 'format' => $format, - ]; - - foreach ($types as $type) { - $methods[] = [ - 'type' => $type, - 'format' => $format, - 'direction' => GraphNavigator::DIRECTION_SERIALIZATION, - 'method' => 'serialize' . $type, - ]; - } - } - - return $methods; - } - - public function serializeDateTime(VisitorInterface $visitor, DateTimeInterface $date, array $type, Context $context) - { - if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) { - return $visitor->visitSimpleString($date->format($this->getFormat($type)), $type, $context); - } - - return $visitor->visitString($date->format($this->getFormat($type)), $type, $context); - } - - public function serializeDateInterval(VisitorInterface $visitor, DateInterval $date, array $type, Context $context) - { - $iso8601DateIntervalString = $this->format($date); - - if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) { - return $visitor->visitSimpleString($iso8601DateIntervalString, $type, $context); - } - - return $visitor->visitString($iso8601DateIntervalString, $type, $context); - } - - public function format(DateInterval $dateInterval): string - { - $format = 'P'; - - if (0 < $dateInterval->y) { - $format .= $dateInterval->y . 'Y'; - } - - if (0 < $dateInterval->m) { - $format .= $dateInterval->m . 'M'; - } - - if (0 < $dateInterval->d) { - $format .= $dateInterval->d . 'D'; - } - - if (0 < $dateInterval->h || 0 < $dateInterval->i || 0 < $dateInterval->s) { - $format .= 'T'; - } - - if (0 < $dateInterval->h) { - $format .= $dateInterval->h . 'H'; - } - - if (0 < $dateInterval->i) { - $format .= $dateInterval->i . 'M'; - } - - if (0 < $dateInterval->s) { - $format .= $dateInterval->s . 'S'; - } - - return $format; - } - - public function deserializeDateTimeFromXml(XmlDeserializationVisitor $unused, $data, array $type) - { - $attributes = $data->attributes('xsi', true); - if (isset($attributes['nil'][0]) && (string) $attributes['nil'][0] === 'true') { - return null; - } - - return $this->parseDateTime($data, $type); - } - - public function deserializeDateTimeFromJson(JsonDeserializationVisitor $unused, $data, array $type) - { - if (null === $data) { - return null; - } - - return $this->parseDateTime($data, $type); - } - - /** - * @param array $type - * - * @return string - * - */ - private function getFormat(array $type): string - { - return $type['params'][0] ?? $this->defaultFormat; - } - - private function parseDateTime($data, array $type) - { - $timezone = isset($type['params'][1]) ? new DateTimeZone($type['params'][1]) : $this->defaultTimezone; - $format = $this->getFormat($type); - $datetime = DateTime::createFromFormat($format, (string) $data, $timezone); - if (false === $datetime) { - throw new RuntimeException(sprintf('Invalid datetime "%s", expected format %s.', $data, $format)); - } - - return $datetime; - } -} diff --git a/src/Shared/Infrastructure/Bus/AsyncRequest.php b/src/Shared/Infrastructure/Bus/AsyncRequest.php deleted file mode 100644 index f7a8e4e5b..000000000 --- a/src/Shared/Infrastructure/Bus/AsyncRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -requestId = $requestId; - $this->status = $status; - $this->response = $response; - } - - public function toArray(): array - { - return [ - 'request_id' => $this->requestId->value(), - 'status' => $this->status->value(), - 'response' => $this->response->values(), - ]; - } -} diff --git a/src/Shared/Infrastructure/Bus/AsyncRequestFinder.php b/src/Shared/Infrastructure/Bus/AsyncRequestFinder.php deleted file mode 100644 index 18710a744..000000000 --- a/src/Shared/Infrastructure/Bus/AsyncRequestFinder.php +++ /dev/null @@ -1,38 +0,0 @@ -pendingRequestsFilePath = $pendingRequestsFilePath; - $this->inProgressRequestsFilePath = $inProgressRequestsFilePath; - $this->processedRequestsFilePath = $processedRequestsFilePath; - } - - public function __invoke(Uuid $requestId): AsyncRequest - { - // Is it in pending? - - // Is it in progress? - - // Has it been processed? - - throw new AsyncRequestNotExists($requestId); - } -} diff --git a/src/Shared/Infrastructure/Bus/AsyncRequestNotExists.php b/src/Shared/Infrastructure/Bus/AsyncRequestNotExists.php deleted file mode 100644 index ebdcb1a1d..000000000 --- a/src/Shared/Infrastructure/Bus/AsyncRequestNotExists.php +++ /dev/null @@ -1,9 +0,0 @@ - is an invalid async request status', $value)); - } -} diff --git a/src/Shared/Infrastructure/Bus/AsyncResponse.php b/src/Shared/Infrastructure/Bus/AsyncResponse.php deleted file mode 100644 index 2a551fadf..000000000 --- a/src/Shared/Infrastructure/Bus/AsyncResponse.php +++ /dev/null @@ -1,20 +0,0 @@ -values = $values; - } - - public function values(): array - { - return $this->values; - } -} diff --git a/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php b/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php new file mode 100644 index 000000000..23564a9e1 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php @@ -0,0 +1,80 @@ + $parameterExtractor->extract($handler); + } + + private static function pipedCallablesReducer(): callable + { + return static function (array $subscribers, DomainEventSubscriber $subscriber): array { + $subscribedEvents = $subscriber::subscribedTo(); + + foreach ($subscribedEvents as $subscribedEvent) { + $subscribers[$subscribedEvent][] = $subscriber; + } + + return $subscribers; + }; + } + + private static function unflatten(): callable + { + return static fn (mixed $value): array => [$value]; + } + + public function extract(object $class): ?string + { + $reflector = new ReflectionClass($class); + $method = $reflector->getMethod('__invoke'); + + if ($this->hasOnlyOneParameter($method)) { + return $this->firstParameterClassFrom($method); + } + + return null; + } + + private function firstParameterClassFrom(ReflectionMethod $method): string + { + /** @var ReflectionNamedType|null $fistParameterType */ + $fistParameterType = $method->getParameters()[0]->getType(); + + if ($fistParameterType === null) { + throw new LogicException('Missing type hint for the first parameter of __invoke'); + } + + return $fistParameterType->getName(); + } + + private function hasOnlyOneParameter(ReflectionMethod $method): bool + { + return $method->getNumberOfParameters() === 1; + } +} diff --git a/src/Shared/Infrastructure/Bus/Command/CommandBusAsync.php b/src/Shared/Infrastructure/Bus/Command/CommandBusAsync.php deleted file mode 100644 index b2dce83cd..000000000 --- a/src/Shared/Infrastructure/Bus/Command/CommandBusAsync.php +++ /dev/null @@ -1,25 +0,0 @@ -pendingRequestsFilePath = $pendingRequestsFilePath; - } - - public function dispatch(Command $command): void - { - file_put_contents($this->pendingRequestsFilePath, apply(new MessageSerializer(), [$command])); - } -} diff --git a/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php b/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php index 197d74056..fdb18e189 100644 --- a/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php +++ b/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php @@ -1,30 +1,18 @@ command = $command; - - parent::__construct(); - } - - public function errorCode(): string - { - return 'command_bus_not_registered_error'; - } - - protected function errorMessage(): string - { - return sprintf('The command <%s> has not been registered', get_class($this->command)); - } + parent::__construct("The command <$commandClass> hasn't a command handler associated"); + } } diff --git a/src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php b/src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php new file mode 100644 index 000000000..4f6bbed0e --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php @@ -0,0 +1,41 @@ +bus = new MessageBus( + [ + new HandleMessageMiddleware( + new HandlersLocator(CallableFirstParameterExtractor::forCallables($commandHandlers)) + ), + ] + ); + } + + public function dispatch(Command $command): void + { + try { + $this->bus->dispatch($command); + } catch (NoHandlerForMessageException) { + throw new CommandNotRegisteredError($command); + } catch (HandlerFailedException $error) { + throw $error->getPrevious() ?? $error; + } + } +} diff --git a/src/Shared/Infrastructure/Bus/Command/SymfonySyncCommandBus.php b/src/Shared/Infrastructure/Bus/Command/SymfonySyncCommandBus.php deleted file mode 100644 index cfd7c6404..000000000 --- a/src/Shared/Infrastructure/Bus/Command/SymfonySyncCommandBus.php +++ /dev/null @@ -1,38 +0,0 @@ -bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator(CallableFirstParameterExtractor::forCallables($commandHandlers)) - ), - ] - ); - } - - public function dispatch(Command $command): void - { - try { - $this->bus->dispatch($command); - } catch (NoHandlerForMessageException $unused) { - throw new CommandNotRegisteredError($command); - } - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php new file mode 100644 index 000000000..3d73fa61b --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php @@ -0,0 +1,27 @@ +mapping->for($eventName); + + return $eventClass::fromPrimitives( + $eventData['data']['attributes']['id'], + $eventData['data']['attributes'], + $eventData['data']['id'], + $eventData['data']['occurred_on'] + ); + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventJsonSerializer.php b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonSerializer.php new file mode 100644 index 000000000..293e5f996 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonSerializer.php @@ -0,0 +1,25 @@ + [ + 'id' => $domainEvent->eventId(), + 'type' => $domainEvent::eventName(), + 'occurred_on' => $domainEvent->occurredOn(), + 'attributes' => array_merge($domainEvent->toPrimitives(), ['id' => $domainEvent->aggregateId()]), + ], + 'meta' => [], + ] + ); + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php b/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php index f1135e20a..697acad8d 100644 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php @@ -1,44 +1,43 @@ mapping = reduce($this->eventsExtractor(), $mapping); - } - - public function for(string $name) - { - return $this->mapping[$name]; - } - - public function all() - { - return $this->mapping; - } - - private function eventsExtractor(): callable - { - return function (array $mapping, DomainEventSubscriber $subscriber) { - return array_merge($mapping, reindex($this->eventNameExtractor(), $subscriber::subscribedTo())); - }; - } - - private function eventNameExtractor(): callable - { - return function (DomainEvent $event): string { - return $event::eventName(); - }; - } + private array $mapping; + + public function __construct(iterable $mapping) + { + $this->mapping = reduce($this->eventsExtractor(), $mapping, []); + } + + public function for(string $name): string + { + if (!isset($this->mapping[$name])) { + throw new RuntimeException("The Domain Event Class for <$name> doesn't exists or have no subscribers"); + } + + return $this->mapping[$name]; + } + + private function eventsExtractor(): callable + { + return fn (array $mapping, DomainEventSubscriber $subscriber): array => array_merge( + $mapping, + reindex($this->eventNameExtractor(), $subscriber::subscribedTo()) + ); + } + + private function eventNameExtractor(): callable + { + return static fn (string $eventClass): string => $eventClass::eventName(); + } } diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberConfig.php b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberConfig.php deleted file mode 100644 index d8375647b..000000000 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberConfig.php +++ /dev/null @@ -1,66 +0,0 @@ - 1, - 'priority' => 0, - 'events_to_process' => 200, - 'enabled' => true, - ]; - private $config; - - public function __construct(array $config) - { - $this->config = array_merge(self::$defaultConfig, $config); - } - - public function name(): string - { - return $this->config['name']; - } - - public function processes() - { - return $this->config['processes']; - } - - public function priority() - { - return $this->config['priority']; - } - - public function eventsToProcess() - { - return $this->config['events_to_process']; - } - - public function subscribedEvents(): array - { - return $this->config['subscribed_events']; - } - - public function isSubscribedToEvent($eventName): bool - { - return in_array($eventName, $this->subscribedEvents(), false); - } - - public function isEnabled(): bool - { - return $this->config['enabled']; - } - - public function enabledString(): string - { - return $this->isEnabled() ? 'true' : 'false'; - } - - public static function blank(): DomainEventSubscriberConfig - { - return new self([]); - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php new file mode 100644 index 000000000..d39aa7651 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php @@ -0,0 +1,50 @@ +mapping = iterator_to_array($mapping); + } + + public function allSubscribedTo(string $eventClass): array + { + $formatted = CallableFirstParameterExtractor::forPipedCallables($this->mapping); + + return $formatted[$eventClass]; + } + + public function withRabbitMqQueueNamed(string $queueName): callable | DomainEventSubscriber + { + $subscriber = search( + static fn (DomainEventSubscriber $subscriber): bool => RabbitMqQueueNameFormatter::format($subscriber) === + $queueName, + $this->mapping + ); + + if ($subscriber === null) { + throw new RuntimeException("There are no subscribers for the <$queueName> queue"); + } + + return $subscriber; + } + + public function all(): array + { + return $this->mapping; + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscribersConfiguration.php b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscribersConfiguration.php deleted file mode 100644 index a68a19e63..000000000 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscribersConfiguration.php +++ /dev/null @@ -1,62 +0,0 @@ -byNameFinder($name), self::$config)); - } - - /** @return DomainEventSubscriberConfig[] */ - public function all(): array - { - return map($this->domainEventConfigCreator(), self::$config); - } - - /** @return DomainEventSubscriberConfig[] */ - public function allWithEvent(string $name): array - { - return map($this->domainEventConfigCreator(), filter($this->containingEvent($name), self::$config)); - } - - private function domainEventConfigCreator(): callable - { - return function (array $configuration): DomainEventSubscriberConfig { - return new DomainEventSubscriberConfig($configuration); - }; - } - - private function byNameFinder(string $name): callable - { - return function (array $config) use ($name): bool { - return $name === $config['name']; - }; - } - - private function containingEvent(string $name): callable - { - return function (array $config) use ($name): bool { - return in_array($name, $config['subscribed_events'], false); - }; - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/Guard/DomainEventDataValidator.php b/src/Shared/Infrastructure/Bus/Event/Guard/DomainEventDataValidator.php deleted file mode 100644 index 45db45ab3..000000000 --- a/src/Shared/Infrastructure/Bus/Event/Guard/DomainEventDataValidator.php +++ /dev/null @@ -1,41 +0,0 @@ -', $eventClass)); - } - } - - private static function areAllParametersValid(array $data, array $rules): bool - { - return self::hasAllParameters($data, $rules) && all(self::parameterIsValid($data), $rules); - } - - private static function hasAllParameters(array $data, array $rules): bool - { - return empty(array_diff_key($rules, $data)); - } - - private static function parameterIsValid(array $data): callable - { - return function (array $meta, string $attribute) use ($data) { - return Validator::isValid($data[$attribute], self::extractType($meta)); - }; - } - - private static function extractType(array $meta) - { - return $meta[0]; - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php b/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php new file mode 100644 index 000000000..db19cb695 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php @@ -0,0 +1,39 @@ +bus = new MessageBus( + [ + new HandleMessageMiddleware( + new HandlersLocator(CallableFirstParameterExtractor::forPipedCallables($subscribers)) + ), + ] + ); + } + + public function publish(DomainEvent ...$events): void + { + foreach ($events as $event) { + try { + $this->bus->dispatch($event); + } catch (NoHandlerForMessageException) { + } + } + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineDomainEventsConsumer.php b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineDomainEventsConsumer.php new file mode 100644 index 000000000..a07e3b09f --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineDomainEventsConsumer.php @@ -0,0 +1,68 @@ +connection = $entityManager->getConnection(); + } + + public function consume(callable $subscribers, int $eventsToConsume): void + { + $events = $this->connection + ->executeQuery("SELECT * FROM domain_events ORDER BY occurred_on ASC LIMIT $eventsToConsume") + ->fetchAllAssociative(); + + each($this->executeSubscribers($subscribers), $events); + + $ids = implode(', ', map($this->idExtractor(), $events)); + + if (!empty($ids)) { + $this->connection->executeStatement("DELETE FROM domain_events WHERE id IN ($ids)"); + } + } + + private function executeSubscribers(callable $subscribers): callable + { + return function (array $rawEvent) use ($subscribers): void { + try { + $domainEventClass = $this->eventMapping->for($rawEvent['name']); + $domainEvent = $domainEventClass::fromPrimitives( + $rawEvent['aggregate_id'], + Utils::jsonDecode($rawEvent['body']), + $rawEvent['id'], + $this->formatDate($rawEvent['occurred_on']) + ); + + $subscribers($domainEvent); + } catch (RuntimeException) { + } + }; + } + + private function formatDate(mixed $stringDate): string + { + return Utils::dateToString(new DateTimeImmutable($stringDate)); + } + + private function idExtractor(): callable + { + return static fn (array $event): string => "'{$event['id']}'"; + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php new file mode 100644 index 000000000..2b21c3897 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php @@ -0,0 +1,49 @@ +connection = $entityManager->getConnection(); + } + + public function publish(DomainEvent ...$events): void + { + each($this->publisher(), $events); + } + + private function publisher(): callable + { + return function (DomainEvent $domainEvent): void { + $id = $this->connection->quote($domainEvent->eventId()); + $aggregateId = $this->connection->quote($domainEvent->aggregateId()); + $name = $this->connection->quote($domainEvent::eventName()); + $body = $this->connection->quote(Utils::jsonEncode($domainEvent->toPrimitives())); + $occurredOn = $this->connection->quote( + Utils::stringToDate($domainEvent->occurredOn())->format(self::DATABASE_TIMESTAMP_FORMAT) + ); + + $this->connection->executeStatement( + <<declareExchange($exchangeName); + $this->declareExchange($retryExchangeName); + $this->declareExchange($deadLetterExchangeName); + + $this->declareQueues($exchangeName, $retryExchangeName, $deadLetterExchangeName, ...$subscribers); + } + + private function declareExchange(string $exchangeName): void + { + $exchange = $this->connection->exchange($exchangeName); + $exchange->setType(AMQP_EX_TYPE_TOPIC); + $exchange->setFlags(AMQP_DURABLE); + $exchange->declareExchange(); + } + + private function declareQueues( + string $exchangeName, + string $retryExchangeName, + string $deadLetterExchangeName, + DomainEventSubscriber ...$subscribers + ): void { + each($this->queueDeclarator($exchangeName, $retryExchangeName, $deadLetterExchangeName), $subscribers); + } + + private function queueDeclarator( + string $exchangeName, + string $retryExchangeName, + string $deadLetterExchangeName + ): callable { + return function (DomainEventSubscriber $subscriber) use ( + $exchangeName, + $retryExchangeName, + $deadLetterExchangeName + ): void { + $queueName = RabbitMqQueueNameFormatter::format($subscriber); + $retryQueueName = RabbitMqQueueNameFormatter::formatRetry($subscriber); + $deadLetterQueueName = RabbitMqQueueNameFormatter::formatDeadLetter($subscriber); + + $queue = $this->declareQueue($queueName); + $retryQueue = $this->declareQueue($retryQueueName, $exchangeName, $queueName, 1000); + $deadLetterQueue = $this->declareQueue($deadLetterQueueName); + + $queue->bind($exchangeName, $queueName); + $retryQueue->bind($retryExchangeName, $queueName); + $deadLetterQueue->bind($deadLetterExchangeName, $queueName); + + foreach ($subscriber::subscribedTo() as $eventClass) { + $queue->bind($exchangeName, $eventClass::eventName()); + } + }; + } + + private function declareQueue( + string $name, + string $deadLetterExchange = null, + string $deadLetterRoutingKey = null, + int $messageTtl = null + ): AMQPQueue { + $queue = $this->connection->queue($name); + + if ($deadLetterExchange !== null) { + $queue->setArgument('x-dead-letter-exchange', $deadLetterExchange); + } + + if ($deadLetterRoutingKey !== null) { + $queue->setArgument('x-dead-letter-routing-key', $deadLetterRoutingKey); + } + + if ($messageTtl !== null) { + $queue->setArgument('x-message-ttl', $messageTtl); + } + + $queue->setFlags(AMQP_DURABLE); + $queue->declareQueue(); + + return $queue; + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqConnection.php b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqConnection.php new file mode 100644 index 000000000..ea3bcb9fd --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqConnection.php @@ -0,0 +1,68 @@ +channel()); + $queue->setName($name); + + self::$queues[$name] = $queue; + } + + return self::$queues[$name]; + } + + public function exchange(string $name): AMQPExchange + { + if (!array_key_exists($name, self::$exchanges)) { + $exchange = new AMQPExchange($this->channel()); + $exchange->setName($name); + + self::$exchanges[$name] = $exchange; + } + + return self::$exchanges[$name]; + } + + private function channel(): AMQPChannel + { + if (!self::$channel?->isConnected()) { + self::$channel = new AMQPChannel($this->connection()); + } + + return self::$channel; + } + + private function connection(): AMQPConnection + { + if (self::$connection === null) { + self::$connection = new AMQPConnection($this->configuration); + } + + if (!self::$connection->isConnected()) { + self::$connection->pconnect(); + } + + return self::$connection; + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqDomainEventsConsumer.php b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqDomainEventsConsumer.php new file mode 100644 index 000000000..ea18ec7ce --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqDomainEventsConsumer.php @@ -0,0 +1,93 @@ +connection->queue($queueName)->consume($this->consumer($subscriber)); + } catch (AMQPQueueException) { + // We don't want to raise an error if there are no messages in the queue + } + } + + private function consumer(callable $subscriber): callable + { + return function (AMQPEnvelope $envelope, AMQPQueue $queue) use ($subscriber): void { + $event = $this->deserializer->deserialize($envelope->getBody()); + + try { + $subscriber($event); + } catch (Throwable $error) { + $this->handleConsumptionError($envelope, $queue); + + throw $error; + } + + $queue->ack($envelope->getDeliveryTag()); + }; + } + + private function handleConsumptionError(AMQPEnvelope $envelope, AMQPQueue $queue): void + { + $this->hasBeenRedeliveredTooMuch($envelope) + ? $this->sendToDeadLetter($envelope, $queue) + : $this->sendToRetry($envelope, $queue); + + $queue->ack($envelope->getDeliveryTag()); + } + + private function hasBeenRedeliveredTooMuch(AMQPEnvelope $envelope): bool + { + return get('redelivery_count', $envelope->getHeaders(), 0) >= $this->maxRetries; + } + + private function sendToDeadLetter(AMQPEnvelope $envelope, AMQPQueue $queue): void + { + $this->sendMessageTo(RabbitMqExchangeNameFormatter::deadLetter($this->exchangeName), $envelope, $queue); + } + + private function sendToRetry(AMQPEnvelope $envelope, AMQPQueue $queue): void + { + $this->sendMessageTo(RabbitMqExchangeNameFormatter::retry($this->exchangeName), $envelope, $queue); + } + + private function sendMessageTo(string $exchangeName, AMQPEnvelope $envelope, AMQPQueue $queue): void + { + $headers = $envelope->getHeaders(); + + $this->connection->exchange($exchangeName)->publish( + $envelope->getBody(), + $queue->getName(), + AMQP_NOPARAM, + [ + 'message_id' => $envelope->getMessageId(), + 'content_type' => $envelope->getContentType(), + 'content_encoding' => $envelope->getContentEncoding(), + 'priority' => $envelope->getPriority(), + 'headers' => assoc($headers, 'redelivery_count', get('redelivery_count', $headers, 0) + 1), + ] + ); + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php new file mode 100644 index 000000000..1d7bc1a89 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php @@ -0,0 +1,56 @@ +publisher(), $events); + } + + private function publisher(): callable + { + return function (DomainEvent $event): void { + try { + $this->publishEvent($event); + } catch (AMQPException) { + $this->failoverPublisher->publish($event); + } + }; + } + + private function publishEvent(DomainEvent $event): void + { + $body = DomainEventJsonSerializer::serialize($event); + $routingKey = $event::eventName(); + $messageId = $event->eventId(); + + $this->connection->exchange($this->exchangeName)->publish( + $body, + $routingKey, + AMQP_NOPARAM, + [ + 'message_id' => $messageId, + 'content_type' => 'application/json', + 'content_encoding' => 'utf-8', + ] + ); + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqExchangeNameFormatter.php b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqExchangeNameFormatter.php new file mode 100644 index 000000000..7e9be1dc9 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqExchangeNameFormatter.php @@ -0,0 +1,18 @@ + Utils::toSnakeCase($text); + } +} diff --git a/src/Shared/Infrastructure/Bus/Event/Serialize/DomainEventUnserializer.php b/src/Shared/Infrastructure/Bus/Event/Serialize/DomainEventUnserializer.php deleted file mode 100644 index 6592ddd18..000000000 --- a/src/Shared/Infrastructure/Bus/Event/Serialize/DomainEventUnserializer.php +++ /dev/null @@ -1,38 +0,0 @@ -eventMapping = $eventMapping; - } - - public function __invoke(string $serializedEvent): DomainEvent - { - $parsedEvent = json_decode($serializedEvent, true); - - $eventName = $parsedEvent['type']; - $eventClass = $this->eventMapping->for($eventName); - - return new $eventClass(get('id', $parsedEvent), reindex($this->toCamel(), $parsedEvent)); - } - - private function toCamel(): callable - { - return function ($unused, $key): string { - return snake_to_camel($key); - }; - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/SubscribersMapping.php b/src/Shared/Infrastructure/Bus/Event/SubscribersMapping.php deleted file mode 100644 index 1ea35a5f4..000000000 --- a/src/Shared/Infrastructure/Bus/Event/SubscribersMapping.php +++ /dev/null @@ -1,25 +0,0 @@ -events = array_merge($this->events, array_values($domainEvents)); - } - - public function publishRecorded(): void - { - each($this->eventPublisher(), $this->popEvents()); - } - - public function publish(DomainEvent ...$domainEvents): void - { - $this->record(...$domainEvents); - - $this->publishRecorded(); - } - - public function popPublishedEvents(): array - { - $events = $this->publishedEvents; - $this->publishedEvents = []; - - return $events; - } - - public function hasEventsToPublish(): bool - { - return count($this->publishedEvents) > 0; - } - - private function eventPublisher(): callable - { - return function (DomainEvent $event): void { - $this->publishedEvents[] = $event; - }; - } - - private function popEvents(): array - { - $events = $this->events; - $this->events = []; - - return $events; - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/SymfonySyncEventBus.php b/src/Shared/Infrastructure/Bus/Event/SymfonySyncEventBus.php deleted file mode 100644 index ad4b199de..000000000 --- a/src/Shared/Infrastructure/Bus/Event/SymfonySyncEventBus.php +++ /dev/null @@ -1,35 +0,0 @@ -bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator( - CallableFirstParameterExtractor::forPipedCallables($subscribers) - ) - ), - ] - ); - } - - public function notify(DomainEvent $event): void - { - $this->bus->dispatch($event); - } -} diff --git a/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php b/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php new file mode 100644 index 000000000..c94edfdcc --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php @@ -0,0 +1,34 @@ +monitor->registry()->getOrRegisterCounter( + $this->appName, + 'domain_event', + 'Domain Events', + ['name'] + ); + + each(fn (DomainEvent $event) => $counter->inc(['name' => $event::eventName()]), $events); + + $this->bus->publish(...$events); + } +} diff --git a/src/Shared/Infrastructure/Bus/Middleware/MessageLoggerMiddleware.php b/src/Shared/Infrastructure/Bus/Middleware/MessageLoggerMiddleware.php deleted file mode 100644 index 9299538ac..000000000 --- a/src/Shared/Infrastructure/Bus/Middleware/MessageLoggerMiddleware.php +++ /dev/null @@ -1,36 +0,0 @@ -logger = $logger; - } - - public function __invoke(Message $message, callable $handler): ?callable - { - $this->logger->debug( - 'New message dispatched', - [ - 'type' => $message->messageType(), - 'name' => $this->nameOf($message), - ] - ); - - return $handler($message); - } - - private function nameOf(Message $message): string - { - return get_class($message); - } -} diff --git a/src/Shared/Infrastructure/Bus/Middleware/Middleware.php b/src/Shared/Infrastructure/Bus/Middleware/Middleware.php deleted file mode 100644 index 03228bdc8..000000000 --- a/src/Shared/Infrastructure/Bus/Middleware/Middleware.php +++ /dev/null @@ -1,12 +0,0 @@ -handlers[$key][] = $handler; - } - - public function find($key): array - { - return $this->handlers[$key]; - } -} diff --git a/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php b/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php new file mode 100644 index 000000000..4812214d0 --- /dev/null +++ b/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php @@ -0,0 +1,41 @@ +bus = new MessageBus( + [ + new HandleMessageMiddleware(new HandlersLocator(CallableFirstParameterExtractor::forCallables($queryHandlers))), + ] + ); + } + + public function ask(Query $query): ?Response + { + try { + /** @var HandledStamp $stamp */ + $stamp = $this->bus->dispatch($query)->last(HandledStamp::class); + + return $stamp->getResult(); + } catch (NoHandlerForMessageException) { + throw new QueryNotRegisteredError($query); + } + } +} diff --git a/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php b/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php index 30306fc46..c79d4859c 100644 --- a/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php +++ b/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php @@ -1,30 +1,18 @@ query = $query; - - parent::__construct(); - } - - public function errorCode(): string - { - return 'query_bus_not_registered_error'; - } - - protected function errorMessage(): string - { - return sprintf('The query <%s> has not been registered', get_class($this->query)); - } + parent::__construct("The query <$queryClass> has no associated query handler"); + } } diff --git a/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBus.php b/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBus.php deleted file mode 100644 index 458c0e471..000000000 --- a/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBus.php +++ /dev/null @@ -1,43 +0,0 @@ -bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator(CallableFirstParameterExtractor::forCallables($queryHandlers)) - ), - ] - ); - } - - public function ask(Query $query): ?Response - { - try { - /** @var HandledStamp $stamp */ - $stamp = $this->bus->dispatch($query)->last(HandledStamp::class); - - return $stamp->getResult(); - } catch (NoHandlerForMessageException $unused) { - throw new QueryNotRegisteredError($query); - } - } -} diff --git a/src/Shared/Infrastructure/Cdc/DatabaseMutationAction.php b/src/Shared/Infrastructure/Cdc/DatabaseMutationAction.php new file mode 100644 index 000000000..fdad67057 --- /dev/null +++ b/src/Shared/Infrastructure/Cdc/DatabaseMutationAction.php @@ -0,0 +1,12 @@ + CourseIdType::class, - LessonIdType::NAME => LessonIdType::class, - QuizStepQuestionsType::NAME => QuizStepQuestionsType::class, - StudentIdType::NAME => StudentIdType::class, - StepIdType::NAME => StepIdType::class, - VideoIdType::NAME => VideoIdType::class, - ]; - - public static function register(): void - { - if (!self::$initialized) { - each(self::registerType(), self::$types); - - self::$initialized = true; - } - } - - private static function registerType(): callable - { - return function ($class, $name): void { - Type::addType($name, $class); - }; - } -} diff --git a/src/Shared/Infrastructure/Doctrine/DatabaseCleaner.php b/src/Shared/Infrastructure/Doctrine/DatabaseCleaner.php deleted file mode 100644 index 0088df3bf..000000000 --- a/src/Shared/Infrastructure/Doctrine/DatabaseCleaner.php +++ /dev/null @@ -1,42 +0,0 @@ -getConnection(); - - $tables = $this->tables($connection); - $truncateTablesSql = $this->truncateDatabaseSql($tables); - - $connection->exec($truncateTablesSql); - } - - private function truncateDatabaseSql(array $tables): string - { - $truncateTables = map($this->truncateTableSql(), $tables); - - return sprintf('SET FOREIGN_KEY_CHECKS = 0; %s SET FOREIGN_KEY_CHECKS = 1;', implode(' ', $truncateTables)); - } - - private function truncateTableSql(): callable - { - return function (array $table): string { - return sprintf('TRUNCATE TABLE `%s`;', first($table)); - }; - } - - private function tables(Connection $connection): array - { - return $connection->query('SHOW TABLES')->fetchAll(); - } -} diff --git a/src/Shared/Infrastructure/Doctrine/DatabaseConnections.php b/src/Shared/Infrastructure/Doctrine/DatabaseConnections.php index f69d71acb..86b62015a 100644 --- a/src/Shared/Infrastructure/Doctrine/DatabaseConnections.php +++ b/src/Shared/Infrastructure/Doctrine/DatabaseConnections.php @@ -1,55 +1,32 @@ connections[$name] = $entityManager; - } - - public function clear(): void - { - each($this->clearer(), $this->connections); - } - - public function allConnectionsClearer(): callable - { - return function (): void { - $this->clear(); - }; - } - - public function truncate(): void - { - apply(new DatabaseCleaner(), array_values($this->connections)); - } - - public function testConnections(): void - { - each($this->connectionTester(), $this->connections); - } - - private function clearer(): callable - { - return function (EntityManager $entityManager) { - $entityManager->clear(); - }; - } - - private function connectionTester(): callable - { - return function (EntityManager $entityManager) { - $entityManager->getConnection()->connect(); - }; - } + private readonly array $connections; + + public function __construct(iterable $connections) + { + $this->connections = Utils::iterableToArray($connections); + } + + public function clear(): void + { + each(fn (EntityManager $entityManager) => $entityManager->clear(), $this->connections); + } + + public function truncate(): void + { + apply(new MySqlDatabaseCleaner(), array_values($this->connections)); + } } diff --git a/src/Shared/Infrastructure/Doctrine/Dbal/DbalCustomTypesRegistrar.php b/src/Shared/Infrastructure/Doctrine/Dbal/DbalCustomTypesRegistrar.php new file mode 100644 index 000000000..e76a46c3b --- /dev/null +++ b/src/Shared/Infrastructure/Doctrine/Dbal/DbalCustomTypesRegistrar.php @@ -0,0 +1,32 @@ +criteria = $criteria; - $this->criteriaToDoctrineFields = $criteriaToDoctrineFields; - $this->hydrators = $hydrators; - } - - public static function convert( - Criteria $criteria, - array $criteriaToDoctrineFields = [], - array $hydrators = [] - ): DoctrineCriteria { - $converter = new self($criteria, $criteriaToDoctrineFields, $hydrators); - - return $converter->convertToDoctrineCriteria(); - } - - public static function convertToCount( - Criteria $criteria, - array $criteriaToDoctrineFields = [], - array $hydrators = [] - ): DoctrineCriteria { - $converter = new self($criteria, $criteriaToDoctrineFields, $hydrators); - - return $converter->convertToDoctrineCriteriaToCount(); - } - - private function convertToDoctrineCriteria(): DoctrineCriteria - { - return new DoctrineCriteria( - $this->buildExpression($this->criteria), - $this->formatOrder($this->criteria), - $this->criteria->offset(), - $this->criteria->limit() - ); - } - - private function convertToDoctrineCriteriaToCount(): DoctrineCriteria - { - return new DoctrineCriteria($this->buildExpression($this->criteria), $this->formatOrder($this->criteria)); - } - - private function buildExpression(Criteria $criteria): ?CompositeExpression - { - if ($criteria->hasFilters()) { - return new CompositeExpression( - CompositeExpression::TYPE_AND, - array_map($this->buildComparison(), $criteria->plainFilters()) - ); - } - - return null; - } - - private function buildComparison(): callable - { - return function (Filter $filter): Comparison { - $field = $this->mapFieldValue($filter->field()); - $value = $this->existsHydratorFor($field) ? - $this->hydrate($field, $filter->value()->value()) : - $filter->value()->value(); - - return new Comparison($field, $filter->operator()->value(), $value); - }; - } - - private function mapFieldValue(FilterField $field) - { - return array_key_exists($field->value(), $this->criteriaToDoctrineFields) ? - $this->criteriaToDoctrineFields[$field->value()] : - $field->value(); - } - - private function formatOrder(Criteria $criteria): ?array - { - if (!$criteria->hasOrder()) { - return null; - } - - return [$this->mapOrderBy($criteria->order()->orderBy()) => $criteria->order()->orderType()]; - } - - private function mapOrderBy(OrderBy $field) - { - return array_key_exists($field->value(), $this->criteriaToDoctrineFields) ? - $this->criteriaToDoctrineFields[$field->value()] : - $field->value(); - } - - private function existsHydratorFor($field): bool - { - return array_key_exists($field, $this->hydrators); - } - - private function hydrate($field, $value) - { - return $this->hydrators[$field]($value); - } -} diff --git a/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php b/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php index c50e40efb..a19a5609e 100644 --- a/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php +++ b/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php @@ -1,75 +1,91 @@ 'CodelyTv\Shared\Domain', - ]; - - public static function create(array $parameters, array $prefixes, $isDevMode, $schemaFile): EntityManagerInterface - { - if (true === $isDevMode) { - static::generateDatabaseIfNotExists($parameters, $schemaFile); - } - - DbalTypesRegistrar::register(); - - return EntityManager::create($parameters, self::createConfiguration($prefixes, $isDevMode)); - } - - private static function generateDatabaseIfNotExists(array $parameters, $schemaFile): void - { - self::ensureSchemaFileExists($schemaFile); - - $databaseName = $parameters['dbname']; - $parametersWithoutDatabaseName = dissoc($parameters, 'dbname'); - $connection = DriverManager::getConnection($parametersWithoutDatabaseName); - $schemaManager = new MySqlSchemaManager($connection); - - if (!self::databaseExists($databaseName, $schemaManager)) { - $schemaManager->createDatabase($databaseName); - $connection->exec(sprintf('USE %s', $databaseName)); - $connection->exec(file_get_contents(realpath($schemaFile))); - } - - $connection->close(); - } - - /** @fixme add ApcuCache config to configuration */ - private static function createConfiguration(array $prefixes, $isDevMode): Configuration - { - $config = Setup::createConfiguration($isDevMode, null, new ArrayCache()); - - $config->setMetadataDriverImpl(new SimplifiedYamlDriver(array_merge(self::$sharedPrefixes, $prefixes))); - - return $config; - } - - private static function databaseExists($databaseName, MySqlSchemaManager $schemaManager): bool - { - return in_array($databaseName, $schemaManager->listDatabases(), true); - } - - private static function ensureSchemaFileExists(string $schemaFile): void - { - if (!file_exists($schemaFile)) { - throw new RuntimeException(sprintf('The file <%s> does not exist', $schemaFile)); - } - } + private static array $sharedPrefixes = [ + __DIR__ . '/../../../Shared/Infrastructure/Persistence/Mappings' => 'CodelyTv\Shared\Domain', + ]; + + public static function create( + array $parameters, + array $contextPrefixes, + bool $isDevMode, + string $schemaFile, + array $dbalCustomTypesClasses + ): EntityManager { + if ($isDevMode) { + self::generateDatabaseIfNotExists($parameters, $schemaFile); + } + + DbalCustomTypesRegistrar::register($dbalCustomTypesClasses); + + $config = self::createConfiguration($contextPrefixes, $isDevMode); + $eventManager = new EventManager(); + + return new EntityManager( + DriverManager::getConnection($parameters, $config, $eventManager), + $config, + $eventManager + ); + } + + private static function generateDatabaseIfNotExists(array $parameters, string $schemaFile): void + { + self::ensureSchemaFileExists($schemaFile); + + $databaseName = $parameters['dbname']; + $parametersWithoutDatabaseName = dissoc($parameters, 'dbname'); + $connection = DriverManager::getConnection($parametersWithoutDatabaseName); + $platform = new MariaDBPlatform(); + $schemaManager = new MySQLSchemaManager($connection, $platform); + + if (!self::databaseExists($databaseName, $schemaManager)) { + $schemaManager->createDatabase($databaseName); + + $connection->executeStatement(sprintf('USE %s', $databaseName)); + $connection->executeStatement(file_get_contents(realpath($schemaFile))); + } + + $connection->close(); + } + + private static function databaseExists(string $databaseName, MySQLSchemaManager $schemaManager): bool + { + return in_array($databaseName, $schemaManager->listDatabases(), true); + } + + private static function ensureSchemaFileExists(string $schemaFile): void + { + if (!file_exists($schemaFile)) { + throw new RuntimeException(sprintf('The file <%s> does not exist', $schemaFile)); + } + } + + private static function createConfiguration(array $contextPrefixes, bool $isDevMode): Configuration + { + $config = ORMSetup::createConfiguration($isDevMode); + + $config->setMetadataDriverImpl(new SimplifiedXmlDriver(array_merge(self::$sharedPrefixes, $contextPrefixes))); + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + + return $config; + } } diff --git a/src/Shared/Infrastructure/Doctrine/DoctrineRepository.php b/src/Shared/Infrastructure/Doctrine/DoctrineRepository.php deleted file mode 100644 index 863882abb..000000000 --- a/src/Shared/Infrastructure/Doctrine/DoctrineRepository.php +++ /dev/null @@ -1,61 +0,0 @@ -entityManager = $entityManager; - } - - protected function entityManager(): EntityManager - { - return $this->entityManager; - } - - protected function persist(AggregateRoot $entity): void - { - $this->entityManager()->persist($entity); - $this->entityManager()->flush($entity); - } - - protected function remove(AggregateRoot $entity): void - { - $this->entityManager()->remove($entity); - $this->entityManager()->flush($entity); - } - - protected function persister(): callable - { - return function (AggregateRoot $entity): void { - $this->persist($entity); - }; - } - - protected function remover(): callable - { - return function (AggregateRoot $entity): void { - $this->remove($entity); - }; - } - - protected function repository($entityClass): EntityRepository - { - return $this->entityManager->getRepository($entityClass); - } - - protected function queryBuilder(): QueryBuilder - { - return $this->entityManager->createQueryBuilder(); - } -} diff --git a/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php new file mode 100644 index 000000000..41480e009 --- /dev/null +++ b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php @@ -0,0 +1,33 @@ +client->index( + [ + 'index' => sprintf('%s_%s', $this->indexPrefix, $aggregateName), + 'id' => $identifier, + 'body' => $plainBody, + ] + ); + } + + public function client(): Client + { + return $this->client; + } + + public function indexPrefix(): string + { + return $this->indexPrefix; + } +} diff --git a/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php new file mode 100644 index 000000000..a5472afb9 --- /dev/null +++ b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php @@ -0,0 +1,60 @@ +setHosts([$host])->build(); + + $this->generateIndexIfNotExists($client, $indexPrefix, $schemasFolder, $environment); + + return new ElasticsearchClient($client, $indexPrefix); + } + + private function generateIndexIfNotExists( + Client $client, + string $indexPrefix, + string $schemasFolder, + string $environment + ): void { + if ($environment !== 'prod') { + return; + } + + $indexes = Utils::filesIn($schemasFolder, '.json'); + + foreach ($indexes as $index) { + $indexName = str_replace('.json', '', sprintf('%s_%s', $indexPrefix, $index)); + + if (!$this->indexExists($client, $indexName)) { + $indexBody = Utils::jsonDecode(file_get_contents("$schemasFolder/$index")); + + $client->indices()->create(['index' => $indexName, 'body' => $indexBody]); + } + } + } + + private function indexExists(Client $client, string $indexName): bool + { + try { + $client->indices()->getSettings(['index' => $indexName]); + + return true; + } catch (Missing404Exception) { + return false; + } + } +} diff --git a/src/Shared/Infrastructure/Jms/CodelyTvSerializerDriver.php b/src/Shared/Infrastructure/Jms/CodelyTvSerializerDriver.php deleted file mode 100644 index 6a0137d39..000000000 --- a/src/Shared/Infrastructure/Jms/CodelyTvSerializerDriver.php +++ /dev/null @@ -1,85 +0,0 @@ -getBaseMetadata(), $this->getMetadata()); - } - - public function getBaseMetadata(): array - { - return $this->baseMetadata; - } - - public function loadMetadataForClass(ReflectionClass $refClass) - { - $metadata = $this->getClassConfiguration($refClass); - - return $this->createMetadata($refClass->getName(), $metadata); - } - - protected function addResourceFile($file): void - { - $this->fileResources[] = $file; - } - - private function getClassConfiguration(ReflectionClass $refClass) - { - $metadata = $this->getDriverMetadata(); - - if (!isset($metadata[$refClass->getName()])) { - throw new InvalidArgumentException( - sprintf('The serializer configuration for the class "%s" does not exist.', $refClass->getName()) - ); - } - - return $metadata[$refClass->getName()]; - } - - private function createMetadata($className, array $config): ClassMetadata - { - $metadata = new ClassMetadata($className); - - $this->addThisFileAsMetadataResourceToBeInvalidatedOnChanges($metadata); - $this->addPropertiesToMetadata($config, $metadata); - - return $metadata; - } - - private function addThisFileAsMetadataResourceToBeInvalidatedOnChanges(ClassMetadata $metadata): void - { - $metadata->fileResources = array_merge($metadata->fileResources, $this->fileResources); - } - - private function addPropertiesToMetadata(array $config, ClassMetadata $metadata): void - { - foreach ($config as $propertyName => $propertyAttributes) { - $propertyMetadata = new PropertyMetadata($metadata->name, $propertyName); - $metadata->addPropertyMetadata($propertyMetadata); - - foreach ($propertyAttributes as $attribute => $value) { - if ('type' === $attribute) { - $propertyMetadata->setType($value); - } else { - $propertyMetadata->$attribute = $value; - } - } - } - } -} diff --git a/src/Shared/Infrastructure/Logger/MonologLogger.php b/src/Shared/Infrastructure/Logger/MonologLogger.php new file mode 100644 index 000000000..1e0928028 --- /dev/null +++ b/src/Shared/Infrastructure/Logger/MonologLogger.php @@ -0,0 +1,27 @@ +logger->info($message, $context); + } + + public function warning(string $message, array $context = []): void + { + $this->logger->warning($message, $context); + } + + public function critical(string $message, array $context = []): void + { + $this->logger->critical($message, $context); + } +} diff --git a/src/Shared/Infrastructure/Monitoring/PrometheusMonitor.php b/src/Shared/Infrastructure/Monitoring/PrometheusMonitor.php new file mode 100644 index 000000000..162c0482e --- /dev/null +++ b/src/Shared/Infrastructure/Monitoring/PrometheusMonitor.php @@ -0,0 +1,23 @@ +registry = new CollectorRegistry(new APC()); + } + + public function registry(): CollectorRegistry + { + return $this->registry; + } +} diff --git a/src/Shared/Infrastructure/Monolog/Formatter/LogstashFormatter.php b/src/Shared/Infrastructure/Monolog/Formatter/LogstashFormatter.php deleted file mode 100644 index 9b99dc805..000000000 --- a/src/Shared/Infrastructure/Monolog/Formatter/LogstashFormatter.php +++ /dev/null @@ -1,64 +0,0 @@ -formatLogstash(parent::format($record)); - - return $this->toJson($message) . PHP_EOL; - } - - private function formatLogstash(array $record): array - { - $message = [ - '@timestamp' => $record['datetime'], - '@version' => 1, - 'host' => gethostname(), - 'tags' => ['codelytv', $record['channel']], - 'message' => $record['message'], - 'severity' => $record['level_name'], - ]; - - if (!empty($record['extra'])) { - foreach ($record['extra'] as $key => $val) { - $message[$key] = $val; - } - } - - if (!empty($record['context'])) { - foreach ($record['context'] as $key => $val) { - $message[$key] = $val; - } - } - - $message['type'] = md5(implode('', array_keys($message))); - - return $message; - } - - /** @param Exception $exception */ - protected function normalizeException($exception): array - { - return [ - 'class' => get_class($exception), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - 'code' => method_exists($exception, 'errorCode') ? $exception->errorCode() : $exception->getCode(), - 'trace' => $exception->getTraceAsString(), - 'message' => $exception->getMessage(), - ]; - } -} diff --git a/src/Shared/Infrastructure/Monolog/Processor/CodelyTvRequestProcessor.php b/src/Shared/Infrastructure/Monolog/Processor/CodelyTvRequestProcessor.php deleted file mode 100644 index 9dfb9d831..000000000 --- a/src/Shared/Infrastructure/Monolog/Processor/CodelyTvRequestProcessor.php +++ /dev/null @@ -1,73 +0,0 @@ -requestStack = $requestStack; - } - - public function __invoke(array $record) - { - $request = $this->requestStack->getMasterRequest(); - - return $request ? $this->addRequestInfo($record, $request) : $record; - } - - private function addRequestInfo(array $record, Request $request): array - { - $record['extra']['request']['method'] = $request->getMethod(); - $record['extra']['request']['url'] = $request->getUri(); - $record['extra']['request']['ip'] = $request->getClientIp(); - $record['extra']['request']['content'] = $this->hashToString($request->request->all()); - $record['extra']['request']['attributes'] = $this->hashToString($request->attributes->all()); - $record['extra']['request']['headers'] = $this->hashToString(array_filter($request->headers->all())); - - return $record; - } - - private function hashToString(array $hash, $format = '%s: %s'): string - { - $elements = []; - - foreach ($hash as $key => $values) { - $elements[] = $this->elementAsString($format, $key, $values); - } - - return implode(', ', $elements); - } - - private function elementAsString($format, $key, $values): string - { - return sprintf($format, $key, is_array($values) ? $this->arrayElementAsString($values) : $values); - } - - private function arrayElementAsString(array $values) - { - return $this->containsAnArray($values) ? - sprintf('[%s]', $this->hashToString($values, '#%s: %s')) : - implode(', ', $values); - } - - private function containsAnArray(array $values): bool - { - return any($this->isArray(), $values); - } - - private function isArray(): callable - { - return function ($value): bool { - return is_array($value); - }; - } -} diff --git a/src/Shared/Infrastructure/Persistence/Course/CourseIdType.php b/src/Shared/Infrastructure/Persistence/Course/CourseIdType.php deleted file mode 100644 index c83b741be..000000000 --- a/src/Shared/Infrastructure/Persistence/Course/CourseIdType.php +++ /dev/null @@ -1,31 +0,0 @@ -value(); - } -} - diff --git a/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php new file mode 100644 index 000000000..88bd57083 --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php @@ -0,0 +1,99 @@ +convertToDoctrineCriteria(); + } + + private function convertToDoctrineCriteria(): DoctrineCriteria + { + return new DoctrineCriteria( + $this->buildExpression($this->criteria), + $this->formatOrder($this->criteria), + $this->criteria->offset(), + $this->criteria->limit() + ); + } + + private function buildExpression(Criteria $criteria): ?CompositeExpression + { + if ($criteria->hasFilters()) { + return new CompositeExpression( + CompositeExpression::TYPE_AND, + array_map($this->buildComparison(), $criteria->plainFilters()) + ); + } + + return null; + } + + private function buildComparison(): callable + { + return function (Filter $filter): Comparison { + $field = $this->mapFieldValue($filter->field()); + $value = $this->existsHydratorFor($field) + ? $this->hydrate($field, $filter->value()->value()) + : $filter->value()->value(); + + return new Comparison($field, $filter->operator()->value, $value); + }; + } + + private function mapFieldValue(FilterField $field): mixed + { + return array_key_exists($field->value(), $this->criteriaToDoctrineFields) + ? $this->criteriaToDoctrineFields[$field->value()] + : $field->value(); + } + + private function formatOrder(Criteria $criteria): ?array + { + if (!$criteria->hasOrder()) { + return null; + } + + return [$this->mapOrderBy($criteria->order()->orderBy()) => $criteria->order()->orderType()]; + } + + private function mapOrderBy(OrderBy $field): mixed + { + return array_key_exists($field->value(), $this->criteriaToDoctrineFields) + ? $this->criteriaToDoctrineFields[$field->value()] + : $field->value(); + } + + private function existsHydratorFor(mixed $field): bool + { + return array_key_exists($field, $this->hydrators); + } + + private function hydrate(mixed $field, string $value): mixed + { + return $this->hydrators[$field]($value); + } +} diff --git a/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineRepository.php b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineRepository.php new file mode 100644 index 000000000..dc6cdd9bb --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineRepository.php @@ -0,0 +1,46 @@ +entityManager; + } + + protected function persist(AggregateRoot $entity): void + { + $this->entityManager()->persist($entity); + $this->entityManager()->flush($entity); + } + + protected function remove(AggregateRoot $entity): void + { + $this->entityManager()->remove($entity); + $this->entityManager()->flush($entity); + } + + /** + * @template T of object + * + * @psalm-param class-string $entityClass + * + * @psalm-return EntityRepository + * + * @throws NotSupported + */ + protected function repository(string $entityClass): EntityRepository + { + return $this->entityManager->getRepository($entityClass); + } +} diff --git a/src/Shared/Infrastructure/Persistence/Doctrine/UuidType.php b/src/Shared/Infrastructure/Persistence/Doctrine/UuidType.php new file mode 100644 index 000000000..1ef8213bc --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Doctrine/UuidType.php @@ -0,0 +1,41 @@ +typeClassName(); + + return new $className($value); + } + + final public function convertToDatabaseValue($value, AbstractPlatform $platform): string + { + /** @var Uuid $value */ + return $value->value(); + } +} diff --git a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticQueryGenerator.php b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticQueryGenerator.php new file mode 100644 index 000000000..885066f5e --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticQueryGenerator.php @@ -0,0 +1,52 @@ +typeFor($filter->operator()); + $termLevel = $this->termLevelFor($filter->operator()); + $valueTemplate = $filter->operator()->isContaining() ? '*%s*' : '%s'; + + return array_merge_recursive( + $query, + [ + $type => [ + $termLevel => [ + $filter->field()->value() => sprintf($valueTemplate, strtolower($filter->value()->value())), + ], + ], + ] + ); + } + + private function typeFor(FilterOperator $operator): string + { + return in_array($operator->value, self::$mustNotFields, true) ? self::MUST_NOT_TYPE : self::MUST_TYPE; + } + + private function termLevelFor(FilterOperator $operator): string + { + return match ($operator) { + FilterOperator::EQUAL => self::TERM_TERM, + FilterOperator::NOT_EQUAL => '!=', + FilterOperator::GT, FilterOperator::LT => self::TERM_RANGE, + FilterOperator::CONTAINS, FilterOperator::NOT_CONTAINS => self::TERM_WILDCARD, + }; + } +} diff --git a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchCriteriaConverter.php b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchCriteriaConverter.php new file mode 100644 index 000000000..257e531b3 --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchCriteriaConverter.php @@ -0,0 +1,53 @@ + array_merge( + ['from' => $criteria->offset() ?: 0, 'size' => $criteria->limit() ?: 1000], + $this->formatQuery($criteria), + $this->formatSort($criteria) + ), + ]; + } + + private function formatQuery(Criteria $criteria): array + { + if ($criteria->hasFilters()) { + return [ + 'query' => [ + 'bool' => reduce(new ElasticQueryGenerator(), $criteria->filters(), []), + ], + ]; + } + + return []; + } + + private function formatSort(Criteria $criteria): array + { + if ($criteria->hasOrder()) { + $order = $criteria->order(); + + return [ + 'sort' => [ + $order->orderBy()->value() => [ + 'order' => $order->orderType()->value, + ], + ], + ]; + } + + return []; + } +} diff --git a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchRepository.php b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchRepository.php new file mode 100644 index 000000000..3236dd248 --- /dev/null +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchRepository.php @@ -0,0 +1,61 @@ +convert($criteria); + + return $this->searchRawElasticsearchQuery($query); + } + + protected function persist(string $id, array $plainBody): void + { + $this->client->persist($this->aggregateName(), $id, $plainBody); + } + + protected function searchAllInElastic(): array + { + return $this->searchRawElasticsearchQuery([]); + } + + protected function searchRawElasticsearchQuery(array $params): array + { + try { + $result = $this->client->client()->search(array_merge(['index' => $this->indexName()], $params)); + + $hits = (array) get_in(['hits', 'hits'], $result, []); + + return map($this->elasticValuesExtractor(), $hits); + } catch (Missing404Exception) { + return []; + } + } + + protected function indexName(): string + { + return sprintf('%s_%s', $this->client->indexPrefix(), $this->aggregateName()); + } + + private function elasticValuesExtractor(): callable + { + return static fn (array $elasticValues): array => $elasticValues['_source']; + } +} diff --git a/src/Shared/Infrastructure/Persistence/Lesson/LessonIdType.php b/src/Shared/Infrastructure/Persistence/Lesson/LessonIdType.php deleted file mode 100644 index ca6b746f2..000000000 --- a/src/Shared/Infrastructure/Persistence/Lesson/LessonIdType.php +++ /dev/null @@ -1,30 +0,0 @@ -value(); - } -} diff --git a/src/Shared/Infrastructure/PhpRandomNumberGenerator.php b/src/Shared/Infrastructure/PhpRandomNumberGenerator.php new file mode 100644 index 000000000..b32a8be34 --- /dev/null +++ b/src/Shared/Infrastructure/PhpRandomNumberGenerator.php @@ -0,0 +1,15 @@ +configuration = $configuration; - } - - public function queue(string $name): AMQPQueue - { - if (!array_key_exists($name, self::$queues)) { - $queue = new AMQPQueue($this->channel()); - $queue->setName($name); - - self::$queues[$name] = $queue; - } - - return self::$queues[$name]; - } - - public function queueBinder(AMQPQueue $queue): callable - { - return function (string $exchange, string $routingKey) use ($queue) { - $queue->bind($exchange, $routingKey); - }; - } - - private function channel(): AMQPChannel - { - return self::$channel = - self::$channel && self::$channel->isConnected() ? self::$channel : new AMQPChannel($this->connection()); - } - - private function connection(): AMQPConnection - { - self::$connection = self::$connection ?: new AMQPConnection($this->configuration); - self::$connection->isConnected() ?: self::$connection->pconnect(); - - return self::$connection; - } -} diff --git a/src/Shared/Infrastructure/RabbitMQ/RabbitMQConsumer.php b/src/Shared/Infrastructure/RabbitMQ/RabbitMQConsumer.php deleted file mode 100644 index c7588b4a9..000000000 --- a/src/Shared/Infrastructure/RabbitMQ/RabbitMQConsumer.php +++ /dev/null @@ -1,107 +0,0 @@ -logger = $logger; - $this->consumer = pipe(new DomainEventUnserializer($eventMapping), $consumer); - } - - public function __invoke(AMQPEnvelope $envelope, AMQPQueue $queue) - { - $queueName = $queue->getName(); - - try { - apply($this->consumer, [$envelope->getBody()]); - - $this->log('Message consumed', $envelope, $queueName, LogLevel::DEBUG); - } catch (Exception $exception) { - $level = $this->hasBeenRedeliveredTooMuch($envelope) ? LogLevel::ERROR : LogLevel::DEBUG; - $this->log('Message consumption failed', $envelope, $queueName, $level, $exception); - // $this->requeue($envelope, $queue, $exception); - } - - $this->ack($envelope, $queue); - } - - private function ack(AMQPEnvelope $envelope, AMQPQueue $queue): void - { - try { - $ack = $queue->ack($envelope->getDeliveryTag()); - - if (false === $ack) { - $this->log('Message has not been acknowledged', $envelope, $queue->getName()); - } - } catch (Exception $exception) { - $this->log('Message has not been acknowledged', $envelope, $queue->getName(), LogLevel::ERROR, $exception); - } - } - - private function hasBeenRedeliveredTooMuch(AMQPEnvelope $envelope): bool - { - return get('redelivery_count', $envelope->getHeaders(), 0) > 500; - } - - private function log( - string $message, - AMQPEnvelope $envelope, - string $queueName, - string $level = LogLevel::ERROR, - Exception $exception = null - ): void { - $this->logger->log( - $level, - $message, - [ - 'envelope' => [ - 'app_id' => $envelope->getAppId(), - 'body' => $envelope->getBody(), - 'content_encoding' => $envelope->getContentEncoding(), - 'content_type' => $envelope->getContentType(), - 'correlation_id' => $envelope->getCorrelationId(), - 'delivery_mode' => $envelope->getDeliveryMode(), - 'delivery_tag' => $envelope->getDeliveryTag(), - 'exchange_name' => $envelope->getExchangeName(), - 'expiration' => $envelope->getExpiration(), - 'headers' => $envelope->getHeaders(), - 'message_id' => $envelope->getMessageId(), - 'priority' => $envelope->getPriority(), - 'reply_to' => $envelope->getReplyTo(), - 'routing_key' => $envelope->getRoutingKey(), - 'timestamp' => $envelope->getTimestamp(), - 'type' => $envelope->getType(), - 'user_id' => $envelope->getUserId(), - 'is_redelivery' => $envelope->isRedelivery(), - ], - 'queue' => $queueName, - 'exception' => $exception ? - [ - 'error' => $exception->getMessage(), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - 'trace' => $exception->getTraceAsString(), - ] : - null, - ] - ); - } -} diff --git a/src/Shared/Infrastructure/RabbitMQ/RabbitMQDomainEventConsumeConnectionFailed.php b/src/Shared/Infrastructure/RabbitMQ/RabbitMQDomainEventConsumeConnectionFailed.php deleted file mode 100644 index 5bed5e92c..000000000 --- a/src/Shared/Infrastructure/RabbitMQ/RabbitMQDomainEventConsumeConnectionFailed.php +++ /dev/null @@ -1,16 +0,0 @@ -connection = $connection; - $this->mapping = $mapping; - $this->logger = $logger; - } - - public function __invoke(callable $subscriber, string $name) - { - $queueName = RabbitMQQueueNameFormatter::format($name); - $queue = $this->queue($queueName); - - $queue->consume( - new RabbitMQConsumer($subscriber, $this->mapping, $this->logger) - ); - } - - private function queue(string $queueName): ?AMQPQueue - { - try { - return $this->connection->queue($queueName); - } catch (Exception $exception) { - $this->logger->error( - 'Domain Event consumption failed at opening a channel', - ['queue' => $queueName, 'exception' => $exception] - ); - - throw new RabbitMQDomainEventConsumeConnectionFailed( - 'Domain Event consumption failed at opening a channel', - $exception - ); - } - } -} diff --git a/src/Shared/Infrastructure/RabbitMQ/RabbitMQQueueNameFormatter.php b/src/Shared/Infrastructure/RabbitMQ/RabbitMQQueueNameFormatter.php deleted file mode 100644 index d3d9ae6cc..000000000 --- a/src/Shared/Infrastructure/RabbitMQ/RabbitMQQueueNameFormatter.php +++ /dev/null @@ -1,15 +0,0 @@ -toString(); + } +} diff --git a/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php b/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php new file mode 100644 index 000000000..0930af5ee --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php @@ -0,0 +1,40 @@ +getRequest(); + $requestContents = $request->getContent(); + + if (!empty($requestContents) && $this->containsHeader($request, 'Content-Type', 'application/json')) { + $jsonData = json_decode($requestContents, true, 512, JSON_THROW_ON_ERROR); + if (!$jsonData) { + throw new HttpException(Response::HTTP_BAD_REQUEST, 'Invalid json data'); + } + $jsonDataLowerCase = []; + foreach ($jsonData as $key => $value) { + $jsonDataLowerCase[preg_replace_callback( + '/_(.)/', + static fn ($matches): string => strtoupper((string) $matches[1]), + (string) $key + )] = $value; + } + $request->request->replace($jsonDataLowerCase); + } + } + + private function containsHeader(Request $request, string $name, string $value): bool + { + return str_starts_with((string) $request->headers->get($name), $value); + } +} diff --git a/src/Shared/Infrastructure/Symfony/ApiController.php b/src/Shared/Infrastructure/Symfony/ApiController.php new file mode 100644 index 000000000..09dd9be51 --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/ApiController.php @@ -0,0 +1,39 @@ + $exceptionHandler->register($exceptionClass, $httpCode), + $this->exceptions() + ); + } + + abstract protected function exceptions(): array; + + protected function ask(Query $query): ?Response + { + return $this->queryBus->ask($query); + } + + protected function dispatch(Command $command): void + { + $this->commandBus->dispatch($command); + } +} diff --git a/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php b/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php new file mode 100644 index 000000000..d913d8766 --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php @@ -0,0 +1,48 @@ +getThrowable(); + + $event->setResponse( + new JsonResponse( + [ + 'code' => $this->exceptionCodeFor($exception), + 'message' => $exception->getMessage(), + ], + $this->exceptionHandler->statusCodeFor($exception::class) + ) + ); + } + + private function exceptionCodeFor(Throwable $error): string + { + $domainErrorClass = DomainError::class; + + return $error instanceof $domainErrorClass + ? $error->errorCode() + : Utils::toSnakeCase($this->extractClassName($error)); + } + + private function extractClassName(object $object): string + { + $reflect = new ReflectionClass($object); + + return $reflect->getShortName(); + } +} diff --git a/src/Shared/Infrastructure/Symfony/ApiExceptionsHttpStatusCodeMapping.php b/src/Shared/Infrastructure/Symfony/ApiExceptionsHttpStatusCodeMapping.php new file mode 100644 index 000000000..8b1ad8a11 --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/ApiExceptionsHttpStatusCodeMapping.php @@ -0,0 +1,36 @@ + Response::HTTP_BAD_REQUEST, + NotFoundHttpException::class => Response::HTTP_NOT_FOUND, + ]; + + public function register(string $exceptionClass, int $statusCode): void + { + $this->exceptions[$exceptionClass] = $statusCode; + } + + public function statusCodeFor(string $exceptionClass): int + { + $statusCode = get($exceptionClass, $this->exceptions, self::DEFAULT_STATUS_CODE); + + if ($statusCode === null) { + throw new InvalidArgumentException("There are no status code mapping for <$exceptionClass>"); + } + + return $statusCode; + } +} diff --git a/src/Shared/Infrastructure/Symfony/BasicHttpAuthMiddleware.php b/src/Shared/Infrastructure/Symfony/BasicHttpAuthMiddleware.php new file mode 100644 index 000000000..0f3ad3aa8 --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/BasicHttpAuthMiddleware.php @@ -0,0 +1,62 @@ +getRequest()->attributes->get('auth', false); + + if ($shouldAuthenticate) { + $user = $event->getRequest()->headers->get('php-auth-user'); + $pass = $event->getRequest()->headers->get('php-auth-pw'); + + $this->hasIntroducedCredentials($user) + ? $this->authenticate($user, $pass, $event) + : $this->askForCredentials($event); + } + } + + private function hasIntroducedCredentials(?string $user): bool + { + return $user !== null; + } + + private function authenticate(string $user, string $pass, RequestEvent $event): void + { + try { + $this->bus->dispatch(new AuthenticateUserCommand($user, $pass)); + + $this->addUserDataToRequest($user, $event); + } catch (InvalidAuthCredentials | InvalidAuthUsername) { + $event->setResponse(new JsonResponse(['error' => 'Invalid credentials'], Response::HTTP_FORBIDDEN)); + } + } + + private function addUserDataToRequest(string $user, RequestEvent $event): void + { + $event->getRequest()->attributes->set('authenticated_username', $user); + } + + private function askForCredentials(RequestEvent $event): void + { + $event->setResponse( + new Response('', Response::HTTP_UNAUTHORIZED, [ + 'WWW-Authenticate' => 'Basic realm="CodelyTV"', + ]) + ); + } +} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/CodelyTvInfrastructureBundle.php b/src/Shared/Infrastructure/Symfony/Bundle/CodelyTvInfrastructureBundle.php deleted file mode 100644 index 58fdf18fb..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/CodelyTvInfrastructureBundle.php +++ /dev/null @@ -1,11 +0,0 @@ -load('infrastructure_extension.yml'); - $loader->load(sprintf('infrastructure_config_%s.yml', $container->getParameter('kernel.environment'))); - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/CallableFirstParameterExtractor.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/CallableFirstParameterExtractor.php deleted file mode 100644 index 1ec90e59d..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/CallableFirstParameterExtractor.php +++ /dev/null @@ -1,74 +0,0 @@ -getMethod('__invoke'); - - if ($this->hasOnlyOneParameter($method)) { - return $this->firstParameterClassFrom($method); - } - - return null; - } - - public static function forCallables(iterable $callables): array - { - return map(self::unflatten(), reindex(self::classExtractor(new self()), $callables)); - } - - public static function forPipedCallables(iterable $callables): array - { - return reduce(self::pipedCallablesReducer(), $callables); - } - - private function firstParameterClassFrom(ReflectionMethod $method): string - { - return $method->getParameters()[0]->getClass()->getName(); - } - - private function hasOnlyOneParameter(ReflectionMethod $method): bool - { - return $method->getNumberOfParameters() === 1; - } - - private static function classExtractor(CallableFirstParameterExtractor $parameterExtractor): callable - { - return function (callable $handler) use ($parameterExtractor): string { - return $parameterExtractor->extract($handler); - }; - } - - private static function pipedCallablesReducer(): callable - { - return function ($subscribers, DomainEventSubscriber $subscriber): array { - $subscribedEvents = $subscriber::subscribedTo(); - - foreach ($subscribedEvents as $subscribedEvent) { - $subscribers[$subscribedEvent][] = $subscriber; - } - - return $subscribers; - }; - } - - private static function unflatten(): callable - { - return function ($value) { - return [$value]; - }; - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DatabasesConnectionCompilerPass.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DatabasesConnectionCompilerPass.php deleted file mode 100644 index 4d8c710ee..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DatabasesConnectionCompilerPass.php +++ /dev/null @@ -1,41 +0,0 @@ -tag = $tag; - } - - public function process(ContainerBuilder $container): void - { - $databasesConnectionsService = $container->findDefinition(self::DATABASE_CONNECTIONS_SERVICE); - $databasesConnectionsIds = $container->findTaggedServiceIds($this->tag); - - each($this->addDatabasesConnections($databasesConnectionsService), $databasesConnectionsIds); - } - - private function addDatabasesConnections(Definition $connectionsService): callable - { - return function (array $unused, string $databaseConnectionServiceId) use ($connectionsService): void { - $connectionsService->addMethodCall( - 'set', - [$databaseConnectionServiceId, new Reference($databaseConnectionServiceId)] - ); - }; - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DomainEventSubscribersConfigurationCompilerPass.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DomainEventSubscribersConfigurationCompilerPass.php deleted file mode 100644 index 9a886e8d1..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/DomainEventSubscribersConfigurationCompilerPass.php +++ /dev/null @@ -1,84 +0,0 @@ -tag = $tag; - } - - public function process(ContainerBuilder $container): void - { - $domainEventConfiguration = $container->findDefinition(self::DOMAIN_EVENT_CONFIGURATION_SERVICE); - $subscribersMapping = $container->findDefinition(self::SUBSCRIBERS_MAPPING_SERVICE); - $subscribersIds = $container->findTaggedServiceIds($this->tag); - - each( - $this->addSubscriberConfiguration($domainEventConfiguration, $subscribersMapping, $container), - $subscribersIds - ); - } - - private function addSubscriberConfiguration( - Definition $domainEventConfiguration, - Definition $subscribersMapping, - ContainerBuilder $container - ): callable { - return function ( - array $attributes, - string $subscriberServiceId - ) use ( - $domainEventConfiguration, - $subscribersMapping, - $container - ): void { - $subscriber = $container->findDefinition($subscriberServiceId); - - $subscriberName = $this->extractSubscriberName($subscriberServiceId); - $subscriberClass = $subscriber->getClass(); - $events = $subscriberClass::subscribedTo(); - - $config = array_merge( - get(0, $attributes, []), - [ - 'name' => $subscriberName, - 'subscribed_events' => map($this->eventNameExtractor(), $events), - ] - ); - - $domainEventConfiguration->addMethodCall('set', [$subscriberClass, $config]); - $subscribersMapping->addMethodCall('add', [$subscriberName, new Reference($subscriberServiceId)]); - }; - } - - private function extractSubscriberName(string $subscriberServiceId) - { - return last(explode('.', $subscriberServiceId)); - } - - private function eventNameExtractor(): callable - { - return function (string $eventClass) { - return $eventClass::eventName(); - }; - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceByTagNotDefined.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceByTagNotDefined.php deleted file mode 100644 index fe046c83f..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceByTagNotDefined.php +++ /dev/null @@ -1,15 +0,0 @@ - transactional definition', $service)); - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceCompilerPass.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceCompilerPass.php deleted file mode 100644 index 9294145c4..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalServiceCompilerPass.php +++ /dev/null @@ -1,50 +0,0 @@ -domainEventPublisherTag = $domainEventPublisherTag; - } - - public function process(ContainerBuilder $container): void - { - $servicesToBeTransactional = $container->findTaggedServiceIds(self::TRANSACTIONAL_TAG); - - each($this->serviceDecorator($container), $servicesToBeTransactional); - } - - private function serviceDecorator(ContainerBuilder $container): callable - { - return function (array $tags, string $serviceToBeTransactional) use ($container): void { - $this->ensureByTagExists($tags, $serviceToBeTransactional); - - $container->register($serviceToBeTransactional . '.transactional', TransactionalWrapper::class) - ->addArgument(new Reference($tags[0][self::BY_TAG])) - ->addArgument(new Reference($this->domainEventPublisherTag)) - ->addArgument(new Reference($serviceToBeTransactional . '.transactional.inner')) - ->setDecoratedService($serviceToBeTransactional) - ->setPublic(false); - }; - } - - private function ensureByTagExists(array $tags, string $serviceToBeTransactional): void - { - if (!array_key_exists(self::BY_TAG, $tags[0])) { - throw new TransactionalServiceByTagNotDefined($serviceToBeTransactional); - } - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalWrapper.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalWrapper.php deleted file mode 100644 index 799629800..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/TransactionalWrapper.php +++ /dev/null @@ -1,51 +0,0 @@ -entityManager = $entityManager; - $this->publisher = $publisher; - $this->service = $service; - } - - public function __invoke(...$args): ?Response - { - $result = null; - - $this->entityManager->beginTransaction(); - - try { - $result = apply($this->service, $args); - - $this->entityManager->flush(); - $this->entityManager->commit(); - } catch (Exception $exception) { - $this->entityManager->close(); - $this->entityManager->rollback(); - - throw new WrongTransaction($exception); - } - - $this->publisher->publishRecorded(); - - return $result; - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/WrongTransaction.php b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/WrongTransaction.php deleted file mode 100644 index c52d986a8..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Compiler/WrongTransaction.php +++ /dev/null @@ -1,25 +0,0 @@ -originalException = $originalException; - - parent::__construct(sprintf('Transaction ended with <%s> exception', get_class($originalException))); - } - - public function originalException(): Exception - { - return $this->originalException; - } -} diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config.yml deleted file mode 100644 index 313460a33..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config.yml +++ /dev/null @@ -1,11 +0,0 @@ -parameters: - - rabbitmq_connection_parameters: - host: '%rabbitmq_host%' - port: '%rabbitmq_port%' - vhost: '%rabbitmq_vhost%' - login: '%rabbitmq_login%' - password: '%rabbitmq_password%' - read_timeout: 60 - write_timeout: 5 - connect_timeout: 1 diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_dev.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_dev.yml deleted file mode 100644 index 923fcf0bf..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_dev.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: infrastructure_config.yml } diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_prod.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_prod.yml deleted file mode 100644 index b1931557f..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_prod.yml +++ /dev/null @@ -1,4 +0,0 @@ -imports: - - { resource: infrastructure_config.yml } - -services: diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_test.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_test.yml deleted file mode 100644 index adfc15dc1..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_config_test.yml +++ /dev/null @@ -1,4 +0,0 @@ -imports: - - { resource: infrastructure_config_dev.yml } - -parameters: diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_extension.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_extension.yml deleted file mode 100644 index 922da643a..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_extension.yml +++ /dev/null @@ -1,3 +0,0 @@ -imports: - - { resource: infrastructure_parameters.yml } - - { resource: infrastructure_services.yml } diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_parameters.yml.dist b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_parameters.yml.dist deleted file mode 100644 index 7c5a935f0..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_parameters.yml.dist +++ /dev/null @@ -1,9 +0,0 @@ -parameters: - best_youtube_channel_ever: 'CodelyTv' - async_command_bus_pending_requests_file_path: '/path/to/the/codelytv_pending_requests.dat' - - rabbitmq_host: 'localhost' - rabbitmq_port: 5672 - rabbitmq_vhost: '/' - rabbitmq_login: 'codelytv' - rabbitmq_password: 'c0d3ly' diff --git a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_services.yml b/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_services.yml deleted file mode 100644 index 9764ef7cf..000000000 --- a/src/Shared/Infrastructure/Symfony/Bundle/DependencyInjection/Resources/infrastructure_services.yml +++ /dev/null @@ -1,23 +0,0 @@ -services: - _defaults: - autoconfigure: true - autowire: true - - CodelyTv\Shared\: - resource: '../../../../../*' - exclude: - - '../../../../../utils.php' - - '../../../../../Infrastructure/Api/*' - - CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventMapping: - arguments: [!tagged codely.mooc.subscriber] - - CodelyTv\Shared\Infrastructure\RabbitMQ\RabbitMQConnection: - arguments: - - '%rabbitmq_connection_parameters%' - - codely.infrastructure.async_command_bus: - class: CodelyTv\Shared\Infrastructure\Bus\Command\CommandBusAsync - arguments: - $pendingRequestsFilePath: '%async_command_bus_pending_requests_file_path%' - lazy: true diff --git a/src/Shared/Infrastructure/Symfony/FlashSession.php b/src/Shared/Infrastructure/Symfony/FlashSession.php new file mode 100644 index 000000000..61bc6a523 --- /dev/null +++ b/src/Shared/Infrastructure/Symfony/FlashSession.php @@ -0,0 +1,42 @@ +getSession()->getFlashBag()->all()); + } + + public function get(string $key, $default = null) + { + if (array_key_exists($key, self::$flashes)) { + return self::$flashes[$key]; + } + + if (array_key_exists($key . '.0', self::$flashes)) { + return self::$flashes[$key . '.0']; + } + + if (array_key_exists($key . '.0.0', self::$flashes)) { + return self::$flashes[$key . '.0.0']; + } + + return $default; + } + + public function has(string $key): bool + { + return array_key_exists($key, self::$flashes) + || array_key_exists($key . '.0', self::$flashes) + || array_key_exists($key . '.0.0', self::$flashes); + } +} diff --git a/src/Shared/Infrastructure/Symfony/KernelCache.php b/src/Shared/Infrastructure/Symfony/KernelCache.php deleted file mode 100644 index 9e14d8cfa..000000000 --- a/src/Shared/Infrastructure/Symfony/KernelCache.php +++ /dev/null @@ -1,11 +0,0 @@ -twig->render($templatePath, $arguments)); + } + + final public function redirect(string $routeName): RedirectResponse + { + return new RedirectResponse($this->router->generate($routeName), 302); + } + + final public function redirectWithMessage(string $routeName, string $message): RedirectResponse + { + $this->addFlashFor('message', [$message]); + + return $this->redirect($routeName); + } + + final public function redirectWithErrors( + string $routeName, + ConstraintViolationListInterface $errors, + Request $request + ): RedirectResponse { + $this->addFlashFor('errors', $this->formatFlashErrors($errors)); + $this->addFlashFor('inputs', $request->request->all()); + + return new RedirectResponse($this->router->generate($routeName), 302); + } + + private function formatFlashErrors(ConstraintViolationListInterface $violations): array + { + $errors = []; + foreach ($violations as $violation) { + $errors[str_replace(['[', ']'], ['', ''], $violation->getPropertyPath())] = $violation->getMessage(); + } + + return $errors; + } + + private function addFlashFor(string $prefix, array $messages): void + { + foreach ($messages as $key => $message) { + $this->requestStack->getSession()->getFlashBag()->set($prefix . '.' . $key, $message); + } + } +} diff --git a/src/Shared/Infrastructure/Uuid/UuidGenerator.php b/src/Shared/Infrastructure/Uuid/UuidGenerator.php deleted file mode 100644 index f5f9aece5..000000000 --- a/src/Shared/Infrastructure/Uuid/UuidGenerator.php +++ /dev/null @@ -1,15 +0,0 @@ -toString(); - } -} diff --git a/src/Shared/utils.php b/src/Shared/utils.php deleted file mode 100644 index ce6c2d463..000000000 --- a/src/Shared/utils.php +++ /dev/null @@ -1,50 +0,0 @@ -getTimestamp(); - $microseconds = $date->format('u'); - $millisecondsOnASecond = 1000; - - return (string) (((float) ($timestamp . '.' . $microseconds)) * $millisecondsOnASecond); -} - -function string_to_date($milliseconds): DateTimeImmutable -{ - $millisecondsOnASecond = 1000; - $asSeconds = (int) floor($milliseconds / $millisecondsOnASecond); - $dateTime = new DateTimeImmutable('@' . ((string) $asSeconds), new DateTimeZone('UTC')); - - return new DateTimeImmutable( - $dateTime->format('Y-m-d\TH:i:s') . - '.' . - sprintf('%03d', $milliseconds % $millisecondsOnASecond) . - '000' . - $dateTime->format('O') - ); -} - -function snake_to_camel($word) -{ - return lcfirst(str_replace('_', '', ucwords($word, '_'))); -} - -function camel_to_snake($word) -{ - return ctype_lower($word) ? $word : strtolower(preg_replace('/([^A-Z\s])([A-Z])/', '$1_$2', $word)); -} - -function map_no_null(callable $fn, $coll) -{ - return filter_null(map($fn, $coll)); -} diff --git a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php new file mode 100644 index 000000000..25787a7db --- /dev/null +++ b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php @@ -0,0 +1,62 @@ +handler = new AuthenticateUserCommandHandler(new UserAuthenticator($this->repository())); + } + + /** @test */ + public function it_should_authenticate_a_valid_user(): void + { + $command = AuthenticateUserCommandMother::create(); + $authUser = AuthUserMother::fromCommand($command); + + $this->shouldSearch($authUser->username(), $authUser); + + $this->dispatch($command, $this->handler); + } + + /** @test */ + public function it_should_throw_an_exception_when_the_user_does_not_exist(): void + { + $this->expectException(InvalidAuthUsername::class); + + $command = AuthenticateUserCommandMother::create(); + $username = AuthUsernameMother::create($command->username()); + + $this->shouldSearch($username); + + $this->dispatch($command, $this->handler); + } + + /** @test */ + public function it_should_throw_an_exception_when_the_password_does_not_math(): void + { + $this->expectException(InvalidAuthCredentials::class); + + $command = AuthenticateUserCommandMother::create(); + $authUser = AuthUserMother::create(username: AuthUsernameMother::create($command->username())); + + $this->shouldSearch($authUser->username(), $authUser); + + $this->dispatch($command, $this->handler); + } +} diff --git a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php new file mode 100644 index 000000000..4418e2b9f --- /dev/null +++ b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php @@ -0,0 +1,24 @@ +value() ?? AuthUsernameMother::create()->value(), + $password?->value() ?? AuthPasswordMother::create()->value() + ); + } +} diff --git a/tests/Backoffice/Auth/AuthModuleUnitTestCase.php b/tests/Backoffice/Auth/AuthModuleUnitTestCase.php new file mode 100644 index 000000000..4a7ad4a7c --- /dev/null +++ b/tests/Backoffice/Auth/AuthModuleUnitTestCase.php @@ -0,0 +1,30 @@ +repository() + ->shouldReceive('search') + ->with($this->similarTo($username)) + ->once() + ->andReturn($authUser); + } + + protected function repository(): AuthRepository | MockInterface + { + return $this->repository ??= $this->mock(AuthRepository::class); + } +} diff --git a/tests/Backoffice/Auth/Domain/AuthPasswordMother.php b/tests/Backoffice/Auth/Domain/AuthPasswordMother.php new file mode 100644 index 000000000..9426c01c6 --- /dev/null +++ b/tests/Backoffice/Auth/Domain/AuthPasswordMother.php @@ -0,0 +1,16 @@ +username()), + AuthPasswordMother::create($command->password()) + ); + } +} diff --git a/tests/Backoffice/Auth/Domain/AuthUsernameMother.php b/tests/Backoffice/Auth/Domain/AuthUsernameMother.php new file mode 100644 index 000000000..988cedff5 --- /dev/null +++ b/tests/Backoffice/Auth/Domain/AuthUsernameMother.php @@ -0,0 +1,16 @@ +service(EntityManager::class)); + } + + protected function elasticRepository(): ElasticsearchBackofficeCourseRepository + { + return $this->service(ElasticsearchBackofficeCourseRepository::class); + } +} diff --git a/tests/Backoffice/Courses/Domain/BackofficeCourseCriteriaMother.php b/tests/Backoffice/Courses/Domain/BackofficeCourseCriteriaMother.php new file mode 100644 index 000000000..58e38d029 --- /dev/null +++ b/tests/Backoffice/Courses/Domain/BackofficeCourseCriteriaMother.php @@ -0,0 +1,26 @@ + 'name', + 'operator' => 'CONTAINS', + 'value' => $text, + ]) + ) + ); + } +} diff --git a/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php b/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php new file mode 100644 index 000000000..9d2ffbe35 --- /dev/null +++ b/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php @@ -0,0 +1,22 @@ +value(), + $name ?? CourseNameMother::create()->value(), + $duration ?? CourseDurationMother::create()->value() + ); + } +} diff --git a/tests/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepositoryTest.php b/tests/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepositoryTest.php new file mode 100644 index 000000000..a45718666 --- /dev/null +++ b/tests/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepositoryTest.php @@ -0,0 +1,66 @@ +elasticRepository()->save(BackofficeCourseMother::create()); + } + + /** @test */ + public function it_should_search_all_existing_courses(): void + { + $existingCourse = BackofficeCourseMother::create(); + $anotherExistingCourse = BackofficeCourseMother::create(); + $existingCourses = [$existingCourse, $anotherExistingCourse]; + + $this->elasticRepository()->save($existingCourse); + $this->elasticRepository()->save($anotherExistingCourse); + + $this->eventually(fn () => $this->assertSimilar($existingCourses, $this->elasticRepository()->searchAll())); + } + + /** @test */ + public function it_should_search_all_existing_courses_with_an_empty_criteria(): void + { + $existingCourse = BackofficeCourseMother::create(); + $anotherExistingCourse = BackofficeCourseMother::create(); + $existingCourses = [$existingCourse, $anotherExistingCourse]; + + $this->elasticRepository()->save($existingCourse); + $this->elasticRepository()->save($anotherExistingCourse); + + $this->eventually( + fn () => $this->assertSimilar($existingCourses, $this->elasticRepository()->matching(CriteriaMother::empty())) + ); + } + + /** @test */ + public function it_should_filter_by_criteria(): void + { + $dddInPhpCourse = BackofficeCourseMother::create(name: 'DDD en PHP'); + $dddInJavaCourse = BackofficeCourseMother::create(name: 'DDD en Java'); + $intellijCourse = BackofficeCourseMother::create(name: 'Exprimiendo Intellij'); + $dddCourses = [$dddInPhpCourse, $dddInJavaCourse]; + + $nameContainsDddCriteria = BackofficeCourseCriteriaMother::nameContains('DDD'); + + $this->elasticRepository()->save($dddInJavaCourse); + $this->elasticRepository()->save($dddInPhpCourse); + $this->elasticRepository()->save($intellijCourse); + + $this->eventually( + fn () => $this->assertSimilar($dddCourses, $this->elasticRepository()->matching($nameContainsDddCriteria)) + ); + } +} diff --git a/tests/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepositoryTest.php b/tests/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepositoryTest.php new file mode 100644 index 000000000..300d01d99 --- /dev/null +++ b/tests/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepositoryTest.php @@ -0,0 +1,64 @@ +mySqlRepository()->save(BackofficeCourseMother::create()); + } + + /** @test */ + public function it_should_search_all_existing_courses(): void + { + $existingCourse = BackofficeCourseMother::create(); + $anotherExistingCourse = BackofficeCourseMother::create(); + $existingCourses = [$existingCourse, $anotherExistingCourse]; + + $this->mySqlRepository()->save($existingCourse); + $this->mySqlRepository()->save($anotherExistingCourse); + + $this->assertSimilar($existingCourses, $this->mySqlRepository()->searchAll()); + } + + /** @test */ + public function it_should_search_all_existing_courses_with_an_empty_criteria(): void + { + $existingCourse = BackofficeCourseMother::create(); + $anotherExistingCourse = BackofficeCourseMother::create(); + $existingCourses = [$existingCourse, $anotherExistingCourse]; + + $this->mySqlRepository()->save($existingCourse); + $this->mySqlRepository()->save($anotherExistingCourse); + $this->clearUnitOfWork(); + + $this->assertSimilar($existingCourses, $this->mySqlRepository()->matching(CriteriaMother::empty())); + } + + /** @test */ + public function it_should_filter_by_criteria(): void + { + $dddInPhpCourse = BackofficeCourseMother::create(name: 'DDD en PHP'); + $dddInJavaCourse = BackofficeCourseMother::create(name: 'DDD en Java'); + $intellijCourse = BackofficeCourseMother::create(name: 'Exprimiendo Intellij'); + $dddCourses = [$dddInPhpCourse, $dddInJavaCourse]; + + $nameContainsDddCriteria = BackofficeCourseCriteriaMother::nameContains('DDD'); + + $this->mySqlRepository()->save($dddInJavaCourse); + $this->mySqlRepository()->save($dddInPhpCourse); + $this->mySqlRepository()->save($intellijCourse); + $this->clearUnitOfWork(); + + $this->assertSimilar($dddCourses, $this->mySqlRepository()->matching($nameContainsDddCriteria)); + } +} diff --git a/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeContextInfrastructureTestCase.php b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeContextInfrastructureTestCase.php new file mode 100644 index 000000000..0292a6623 --- /dev/null +++ b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeContextInfrastructureTestCase.php @@ -0,0 +1,30 @@ +service(ElasticsearchClient::class), + $this->service(EntityManager::class) + ); + + $arranger->arrange(); + } + + protected function kernelClass(): string + { + return BackofficeBackendKernel::class; + } +} diff --git a/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeEnvironmentArranger.php b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeEnvironmentArranger.php new file mode 100644 index 000000000..33a33ea05 --- /dev/null +++ b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeEnvironmentArranger.php @@ -0,0 +1,26 @@ +elasticsearchClient]); + apply(new MySqlDatabaseCleaner(), [$this->entityManager]); + } + + public function close(): void {} +} diff --git a/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php b/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php new file mode 100644 index 000000000..4fe82e139 --- /dev/null +++ b/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php @@ -0,0 +1,37 @@ +handler = new CreateCourseCommandHandler(new CourseCreator($this->repository(), $this->eventBus())); + } + + /** @test */ + public function it_should_create_a_valid_course(): void + { + $command = CreateCourseCommandMother::create(); + + $course = CourseMother::fromRequest($command); + $domainEvent = CourseCreatedDomainEventMother::fromCourse($course); + + $this->shouldSave($course); + $this->shouldPublishDomainEvent($domainEvent); + + $this->dispatch($command, $this->handler); + } +} diff --git a/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php b/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php new file mode 100644 index 000000000..9d4b31fea --- /dev/null +++ b/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php @@ -0,0 +1,28 @@ +value() ?? CourseIdMother::create()->value(), + $name?->value() ?? CourseNameMother::create()->value(), + $duration?->value() ?? CourseDurationMother::create()->value() + ); + } +} diff --git a/tests/Mooc/Courses/Application/Update/CourseRenamerTest.php b/tests/Mooc/Courses/Application/Update/CourseRenamerTest.php new file mode 100644 index 000000000..7890b36f7 --- /dev/null +++ b/tests/Mooc/Courses/Application/Update/CourseRenamerTest.php @@ -0,0 +1,51 @@ +renamer = new CourseRenamer($this->repository(), $this->eventBus()); + } + + /** @test */ + public function it_should_rename_an_existing_course(): void + { + $course = CourseMother::create(); + $newName = CourseNameMother::create(); + $renamedCourse = DuplicatorMother::with($course, ['name' => $newName]); + + $this->shouldSearch($course->id(), $course); + $this->shouldSave($renamedCourse); + $this->shouldNotPublishDomainEvent(); + + $this->renamer->__invoke($course->id(), $newName); + } + + /** @test */ + public function it_should_throw_an_exception_when_the_course_not_exist(): void + { + $this->expectException(CourseNotExist::class); + + $id = CourseIdMother::create(); + + $this->shouldSearch($id, null); + + $this->renamer->__invoke($id, CourseNameMother::create()); + } +} diff --git a/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php b/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php new file mode 100644 index 000000000..67ad24223 --- /dev/null +++ b/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php @@ -0,0 +1,16 @@ +service(CourseRepository::class); + } +} diff --git a/tests/Mooc/Courses/CoursesModuleUnitTestCase.php b/tests/Mooc/Courses/CoursesModuleUnitTestCase.php new file mode 100644 index 000000000..b4d269546 --- /dev/null +++ b/tests/Mooc/Courses/CoursesModuleUnitTestCase.php @@ -0,0 +1,39 @@ +repository() + ->shouldReceive('save') + ->with($this->similarTo($course)) + ->once() + ->andReturnNull(); + } + + protected function shouldSearch(CourseId $id, ?Course $course): void + { + $this->repository() + ->shouldReceive('search') + ->with($this->similarTo($id)) + ->once() + ->andReturn($course); + } + + protected function repository(): CourseRepository | MockInterface + { + return $this->repository ??= $this->mock(CourseRepository::class); + } +} diff --git a/tests/Mooc/Courses/Domain/CourseCreatedDomainEventMother.php b/tests/Mooc/Courses/Domain/CourseCreatedDomainEventMother.php new file mode 100644 index 000000000..b04f7109d --- /dev/null +++ b/tests/Mooc/Courses/Domain/CourseCreatedDomainEventMother.php @@ -0,0 +1,31 @@ +value() ?? CourseIdMother::create()->value(), + $name?->value() ?? CourseNameMother::create()->value(), + $duration?->value() ?? CourseDurationMother::create()->value() + ); + } + + public static function fromCourse(Course $course): CourseCreatedDomainEvent + { + return self::create($course->id(), $course->name(), $course->duration()); + } +} diff --git a/tests/Mooc/Courses/Domain/CourseDurationMother.php b/tests/Mooc/Courses/Domain/CourseDurationMother.php new file mode 100644 index 000000000..2cfc68c06 --- /dev/null +++ b/tests/Mooc/Courses/Domain/CourseDurationMother.php @@ -0,0 +1,26 @@ +id()), + CourseNameMother::create($request->name()), + CourseDurationMother::create($request->duration()) + ); + } +} diff --git a/tests/Mooc/Courses/Domain/CourseNameMother.php b/tests/Mooc/Courses/Domain/CourseNameMother.php new file mode 100644 index 000000000..72c4e7e02 --- /dev/null +++ b/tests/Mooc/Courses/Domain/CourseNameMother.php @@ -0,0 +1,16 @@ +repository()->save($course); + } + + /** @test */ + public function it_should_return_an_existing_course(): void + { + $course = CourseMother::create(); + + $this->repository()->save($course); + + $this->assertEquals($course, $this->repository()->search($course->id())); + } + + /** @test */ + public function it_should_not_return_a_non_existing_course(): void + { + $this->assertNull($this->repository()->search(CourseIdMother::create())); + } +} diff --git a/tests/Mooc/CoursesCounter/Application/Find/CoursesCounterResponseMother.php b/tests/Mooc/CoursesCounter/Application/Find/CoursesCounterResponseMother.php new file mode 100644 index 000000000..07a588148 --- /dev/null +++ b/tests/Mooc/CoursesCounter/Application/Find/CoursesCounterResponseMother.php @@ -0,0 +1,17 @@ +value() ?? CoursesCounterTotalMother::create()->value()); + } +} diff --git a/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php b/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php new file mode 100644 index 000000000..2c8bc2ef5 --- /dev/null +++ b/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php @@ -0,0 +1,46 @@ +handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder($this->repository())); + } + + /** @test */ + public function it_should_find_an_existing_courses_counter(): void + { + $counter = CoursesCounterMother::create(); + $query = new FindCoursesCounterQuery(); + $response = CoursesCounterResponseMother::create($counter->total()); + + $this->shouldSearch($counter); + + $this->assertAskResponse($response, $query, $this->handler); + } + + /** @test */ + public function it_should_throw_an_exception_when_courses_counter_does_not_exists(): void + { + $query = new FindCoursesCounterQuery(); + + $this->shouldSearch(null); + + $this->assertAskThrowsException(CoursesCounterNotExist::class, $query, $this->handler); + } +} diff --git a/tests/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreatedTest.php b/tests/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreatedTest.php new file mode 100644 index 000000000..b81642fc2 --- /dev/null +++ b/tests/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreatedTest.php @@ -0,0 +1,74 @@ +subscriber = new IncrementCoursesCounterOnCourseCreated( + new CoursesCounterIncrementer($this->repository(), $this->uuidGenerator(), $this->eventBus()) + ); + } + + /** @test */ + public function it_should_initialize_a_new_counter(): void + { + $event = CourseCreatedDomainEventMother::create(); + + $courseId = CourseIdMother::create($event->aggregateId()); + $newCounter = CoursesCounterMother::withOne($courseId); + $domainEvent = CoursesCounterIncrementedDomainEventMother::fromCounter($newCounter); + + $this->shouldSearch(null); + $this->shouldGenerateUuid($newCounter->id()->value()); + $this->shouldSave($newCounter); + $this->shouldPublishDomainEvent($domainEvent); + + $this->notify($event, $this->subscriber); + } + + /** @test */ + public function it_should_increment_an_existing_counter(): void + { + $event = CourseCreatedDomainEventMother::create(); + + $courseId = CourseIdMother::create($event->aggregateId()); + $existingCounter = CoursesCounterMother::create(); + $incrementedCounter = CoursesCounterMother::incrementing($existingCounter, $courseId); + $domainEvent = CoursesCounterIncrementedDomainEventMother::fromCounter($incrementedCounter); + + $this->shouldSearch($existingCounter); + $this->shouldSave($incrementedCounter); + $this->shouldPublishDomainEvent($domainEvent); + + $this->notify($event, $this->subscriber); + } + + /** @test */ + public function it_should_not_increment_an_already_incremented_course(): void + { + $event = CourseCreatedDomainEventMother::create(); + + $courseId = CourseIdMother::create($event->aggregateId()); + $existingCounter = CoursesCounterMother::withOne($courseId); + + $this->shouldSearch($existingCounter); + + $this->notify($event, $this->subscriber); + } +} diff --git a/tests/Mooc/CoursesCounter/CoursesCounterModuleUnitTestCase.php b/tests/Mooc/CoursesCounter/CoursesCounterModuleUnitTestCase.php new file mode 100644 index 000000000..6937e3902 --- /dev/null +++ b/tests/Mooc/CoursesCounter/CoursesCounterModuleUnitTestCase.php @@ -0,0 +1,37 @@ +repository() + ->shouldReceive('save') + ->once() + ->with($this->similarTo($course)) + ->andReturnNull(); + } + + protected function shouldSearch(?CoursesCounter $counter): void + { + $this->repository() + ->shouldReceive('search') + ->once() + ->andReturn($counter); + } + + protected function repository(): CoursesCounterRepository | MockInterface + { + return $this->repository ??= $this->mock(CoursesCounterRepository::class); + } +} diff --git a/tests/Mooc/CoursesCounter/Domain/CoursesCounterIdMother.php b/tests/Mooc/CoursesCounter/Domain/CoursesCounterIdMother.php new file mode 100644 index 000000000..98bf659a1 --- /dev/null +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterIdMother.php @@ -0,0 +1,16 @@ +value() ?? CoursesCounterIdMother::create()->value(), + $total?->value() ?? CoursesCounterTotalMother::create()->value() + ); + } + + public static function fromCounter(CoursesCounter $counter): CoursesCounterIncrementedDomainEvent + { + return self::create($counter->id(), $counter->total()); + } +} diff --git a/tests/Mooc/CoursesCounter/Domain/CoursesCounterMother.php b/tests/Mooc/CoursesCounter/Domain/CoursesCounterMother.php new file mode 100644 index 000000000..d5200a5cd --- /dev/null +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterMother.php @@ -0,0 +1,41 @@ + CourseIdMother::create()) + ); + } + + public static function withOne(CourseId $courseId): CoursesCounter + { + return self::create(CoursesCounterIdMother::create(), CoursesCounterTotalMother::one(), $courseId); + } + + public static function incrementing(CoursesCounter $existingCounter, CourseId $courseId): CoursesCounter + { + return self::create( + $existingCounter->id(), + CoursesCounterTotalMother::create($existingCounter->total()->value() + 1), + ...array_merge($existingCounter->existingCourses(), [$courseId]) + ); + } +} diff --git a/tests/Mooc/CoursesCounter/Domain/CoursesCounterTotalMother.php b/tests/Mooc/CoursesCounter/Domain/CoursesCounterTotalMother.php new file mode 100644 index 000000000..0c29d090a --- /dev/null +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterTotalMother.php @@ -0,0 +1,26 @@ +classes(Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true)) + ->canOnlyDependOn() + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ + // Itself + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true), + // Shared + Selector::inNamespace('CodelyTv\Shared\Domain'), + ])) + ->because('mooc domain can only import itself and shared domain'); + } + + public function test_mooc_application_should_only_import_itself_and_domain(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Application/', true)) + ->canOnlyDependOn() + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ + // Itself + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Application/', true), + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true), + // Shared + Selector::inNamespace('CodelyTv\Shared'), + ])) + ->because('mooc application can only import itself and shared'); + } + + public function test_mooc_infrastructure_should_not_import_other_contexts_beside_shared(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('CodelyTv\Mooc')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('CodelyTv')) + ->excluding( + // Itself + Selector::inNamespace('CodelyTv\Mooc'), + // Shared + Selector::inNamespace('CodelyTv\Shared'), + ); + } +} diff --git a/tests/Mooc/Shared/Domain/.gitkeep b/tests/Mooc/Shared/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php new file mode 100644 index 000000000..fb4bef7bf --- /dev/null +++ b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php @@ -0,0 +1,35 @@ +service(EntityManager::class)); + + $arranger->arrange(); + } + + protected function tearDown(): void + { + $arranger = new MoocEnvironmentArranger($this->service(EntityManager::class)); + + $arranger->close(); + + parent::tearDown(); + } + + protected function kernelClass(): string + { + return MoocBackendKernel::class; + } +} diff --git a/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocEnvironmentArranger.php b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocEnvironmentArranger.php new file mode 100644 index 000000000..39ddf5ef5 --- /dev/null +++ b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocEnvironmentArranger.php @@ -0,0 +1,23 @@ +entityManager]); + } + + public function close(): void {} +} diff --git a/tests/Mooc/Steps/Domain/Exercise/ExerciseStepContentMother.php b/tests/Mooc/Steps/Domain/Exercise/ExerciseStepContentMother.php new file mode 100644 index 000000000..eac56c22f --- /dev/null +++ b/tests/Mooc/Steps/Domain/Exercise/ExerciseStepContentMother.php @@ -0,0 +1,16 @@ + QuizStepQuestionMother::create() + ) : $questions; + + return new QuizStep( + $id ?? StepIdMother::create(), + $title ?? StepTitleMother::create(), + $duration ?? StepDurationMother::create(), + ...$stepQuestions + ); + } +} diff --git a/tests/Mooc/Steps/Domain/Quiz/QuizStepQuestionMother.php b/tests/Mooc/Steps/Domain/Quiz/QuizStepQuestionMother.php new file mode 100644 index 000000000..04fcd8005 --- /dev/null +++ b/tests/Mooc/Steps/Domain/Quiz/QuizStepQuestionMother.php @@ -0,0 +1,20 @@ + WordMother::create()) + ); + } +} diff --git a/tests/Mooc/Steps/Domain/StepDurationMother.php b/tests/Mooc/Steps/Domain/StepDurationMother.php new file mode 100644 index 000000000..73ce6842d --- /dev/null +++ b/tests/Mooc/Steps/Domain/StepDurationMother.php @@ -0,0 +1,16 @@ +repository()->save($step); + } + + /** + * @test + * @dataProvider steps + */ + public function it_should_search_an_existing_step(Step $step): void + { + $this->repository()->save($step); + + $this->assertEquals($step, $this->repository()->search($step->id)); + } + + /** + * @test + * @dataProvider steps + */ + public function it_should_delete_an_existing_step(Step $step): void + { + $this->repository()->save($step); + $this->repository()->delete($step); + + $this->assertNull($this->repository()->search($step->id)); + } + + public function steps(): array + { + return [ + 'video' => [VideoStepMother::create()], + 'exercise' => [ExerciseStepMother::create()], + 'quiz' => [QuizStepMother::create()], + ]; + } +} diff --git a/tests/Mooc/Steps/StepsModuleInfrastructureTestCase.php b/tests/Mooc/Steps/StepsModuleInfrastructureTestCase.php new file mode 100644 index 000000000..8d0eb52ca --- /dev/null +++ b/tests/Mooc/Steps/StepsModuleInfrastructureTestCase.php @@ -0,0 +1,16 @@ +service(StepRepository::class); + } +} diff --git a/tests/Mooc/Videos/Application/.gitkeep b/tests/Mooc/Videos/Application/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Mooc/Videos/Domain/.gitkeep b/tests/Mooc/Videos/Domain/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Mooc/Videos/Infrastructure/.gitkeep b/tests/Mooc/Videos/Infrastructure/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Shared/Domain/Criteria/CriteriaMother.php b/tests/Shared/Domain/Criteria/CriteriaMother.php new file mode 100644 index 000000000..b46d45089 --- /dev/null +++ b/tests/Shared/Domain/Criteria/CriteriaMother.php @@ -0,0 +1,26 @@ +getName()])) { + $property->setAccessible(true); + $property->setValue($duplicated, $newParams[$property->getName()]); + } + }, + $reflection->getProperties() + ); + + return $duplicated; + } +} diff --git a/tests/Shared/Domain/IntegerMother.php b/tests/Shared/Domain/IntegerMother.php new file mode 100644 index 000000000..910a08cc8 --- /dev/null +++ b/tests/Shared/Domain/IntegerMother.php @@ -0,0 +1,23 @@ +numberBetween($min, $max); + } + + public static function lessThan(int $max): int + { + return self::between(1, $max); + } +} diff --git a/tests/Shared/Domain/MotherCreator.php b/tests/Shared/Domain/MotherCreator.php new file mode 100644 index 000000000..f2da13923 --- /dev/null +++ b/tests/Shared/Domain/MotherCreator.php @@ -0,0 +1,18 @@ +randomElement($elements); + } +} diff --git a/tests/Shared/Domain/Repeater.php b/tests/Shared/Domain/Repeater.php new file mode 100644 index 000000000..19781e14f --- /dev/null +++ b/tests/Shared/Domain/Repeater.php @@ -0,0 +1,20 @@ +evaluate($actual, '', true); + } + + public static function assertSimilar(mixed $expected, mixed $actual): void + { + $constraint = new CodelyTvConstraintIsSimilar($expected); + + $constraint->evaluate($actual); + } + + public static function similarTo(mixed $value, float $delta = 0.0): CodelyTvMatcherIsSimilar + { + return new CodelyTvMatcherIsSimilar($value, $delta); + } +} diff --git a/tests/Shared/Domain/UuidMother.php b/tests/Shared/Domain/UuidMother.php new file mode 100644 index 000000000..20bd3516c --- /dev/null +++ b/tests/Shared/Domain/UuidMother.php @@ -0,0 +1,13 @@ +unique()->uuid; + } +} diff --git a/tests/Shared/Domain/WordMother.php b/tests/Shared/Domain/WordMother.php new file mode 100644 index 000000000..4bf988e35 --- /dev/null +++ b/tests/Shared/Domain/WordMother.php @@ -0,0 +1,13 @@ +word; + } +} diff --git a/tests/Shared/Infrastructure/ArchitectureTest.php b/tests/Shared/Infrastructure/ArchitectureTest.php new file mode 100644 index 000000000..6a82b2b18 --- /dev/null +++ b/tests/Shared/Infrastructure/ArchitectureTest.php @@ -0,0 +1,40 @@ +sessionHelper = new MinkHelper($this->minkSession); + $this->request = new MinkSessionRequestHelper(new MinkHelper($minkSession)); + } + + /** + * @Given I send a :method request to :url + */ + public function iSendARequestTo(string $method, string $url): void + { + $this->request->sendRequest($method, $this->locatePath($url)); + } + + /** + * @Given I send a :method request to :url with body: + */ + public function iSendARequestToWithBody(string $method, string $url, PyStringNode $body): void + { + $this->request->sendRequestWithPyStringNode($method, $this->locatePath($url), $body); + } + + /** + * @Then the response content should be: + */ + public function theResponseContentShouldBe(PyStringNode $expectedResponse): void + { + $expected = $this->sanitizeOutput($expectedResponse->getRaw()); + $actual = $this->sanitizeOutput($this->sessionHelper->getResponse()); + + if ($expected === false || $actual === false) { + throw new RuntimeException('The outputs could not be parsed as JSON'); + } + + if ($expected !== $actual) { + throw new RuntimeException( + sprintf("The outputs does not match!\n\n-- Expected:\n%s\n\n-- Actual:\n%s", $expected, $actual) + ); + } + } + + /** + * @Then the response should be empty + */ + public function theResponseShouldBeEmpty(): void + { + $actual = trim($this->sessionHelper->getResponse()); + + if (!empty($actual)) { + throw new RuntimeException(sprintf("The outputs is not empty, Actual:\n%s", $actual)); + } + } + + /** + * @Then print last api response + */ + public function printApiResponse(): void + { + print_r($this->sessionHelper->getResponse()); + } + + /** + * @Then print response headers + */ + public function printResponseHeaders(): void + { + print_r($this->sessionHelper->getResponseHeaders()); + } + + /** + * @Then the response status code should be :expectedResponseCode + */ + public function theResponseStatusCodeShouldBe(mixed $expectedResponseCode): void + { + if ($this->minkSession->getStatusCode() !== (int) $expectedResponseCode) { + throw new RuntimeException( + sprintf( + 'The status code <%s> does not match the expected <%s>', + $this->minkSession->getStatusCode(), + $expectedResponseCode + ) + ); + } + } + + private function sanitizeOutput(string $output): false | string + { + return json_encode(json_decode(trim($output), true, 512, JSON_THROW_ON_ERROR), JSON_THROW_ON_ERROR); + } +} diff --git a/tests/Shared/Infrastructure/Behat/ApplicationFeatureContext.php b/tests/Shared/Infrastructure/Behat/ApplicationFeatureContext.php new file mode 100644 index 000000000..29d6788a8 --- /dev/null +++ b/tests/Shared/Infrastructure/Behat/ApplicationFeatureContext.php @@ -0,0 +1,37 @@ +connections->clear(); + $this->connections->truncate(); + } + + /** + * @Given /^I send an event to the event bus:$/ + */ + public function iSendAnEventToTheEventBus(PyStringNode $event): void + { + $domainEvent = $this->deserializer->deserialize($event->getRaw()); + + $this->bus->publish($domainEvent); + } +} diff --git a/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php b/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php new file mode 100644 index 000000000..d009d2a79 --- /dev/null +++ b/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php @@ -0,0 +1,9 @@ +commandBus = new InMemorySymfonyCommandBus([$this->commandHandler()]); + } + + /** @test */ + public function it_should_be_able_to_handle_a_command(): void + { + $this->expectException(RuntimeException::class); + + $this->commandBus->dispatch(new FakeCommand()); + } + + /** @test */ + public function it_should_raise_an_exception_dispatching_a_non_registered_command(): void + { + $this->expectException(CommandNotRegisteredError::class); + + $this->commandBus->dispatch($this->command()); + } + + private function commandHandler(): object + { + return new class() { + public function __invoke(FakeCommand $command): never + { + throw new RuntimeException('This works fine!'); + } + }; + } + + private function command(): Command | MockInterface + { + return $this->mock(Command::class); + } +} diff --git a/tests/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBusTest.php b/tests/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBusTest.php new file mode 100644 index 000000000..ca17d6ceb --- /dev/null +++ b/tests/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBusTest.php @@ -0,0 +1,51 @@ +bus = new MySqlDoctrineEventBus($this->service(EntityManager::class)); + $this->consumer = new MySqlDoctrineDomainEventsConsumer( + $this->service(EntityManager::class), + $this->service(DomainEventMapping::class) + ); + } + + /** @test */ + public function it_should_publish_and_consume_domain_events_from_msql(): void + { + $domainEvent = CourseCreatedDomainEventMother::create(); + $anotherDomainEvent = CoursesCounterIncrementedDomainEventMother::create(); + + $this->bus->publish($domainEvent, $anotherDomainEvent); + + $this->consumer->consume( + subscribers: fn (DomainEvent ...$expectedEvents) => $this->assertContainsEquals($domainEvent, $expectedEvents), + eventsToConsume: 2 + ); + } + + protected function kernelClass(): string + { + return MoocBackendKernel::class; + } +} diff --git a/tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php new file mode 100644 index 000000000..b3d5b09e8 --- /dev/null +++ b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php @@ -0,0 +1,181 @@ +connection = $this->service(RabbitMqConnection::class); + + $this->exchangeName = 'test_domain_events'; + $this->configurer = new RabbitMqConfigurer($this->connection); + $this->publisher = new RabbitMqEventBus( + $this->connection, + $this->exchangeName, + $this->service(MySqlDoctrineEventBus::class) + ); + $this->consumer = new RabbitMqDomainEventsConsumer( + $this->connection, + $this->service(DomainEventJsonDeserializer::class), + $this->exchangeName, + $maxRetries = 1 + ); + $this->fakeSubscriber = new TestAllWorksOnRabbitMqEventsPublished(); + $this->consumerHasBeenExecuted = false; + + $this->cleanEnvironment($this->connection); + } + + /** @test */ + public function it_should_publish_and_consume_domain_events_from_rabbitmq(): void + { + $domainEvent = CourseCreatedDomainEventMother::create(); + + $this->configurer->configure($this->exchangeName, $this->fakeSubscriber); + + $this->publisher->publish($domainEvent); + + $this->consumer->consume( + $this->assertConsumer($domainEvent), + RabbitMqQueueNameFormatter::format($this->fakeSubscriber) + ); + + $this->assertTrue($this->consumerHasBeenExecuted); + } + + /** @test */ + public function it_should_throw_an_exception_consuming_non_existing_domain_events(): void + { + $this->expectException(RuntimeException::class); + + $domainEvent = CoursesCounterIncrementedDomainEventMother::create(); + + $this->configurer->configure($this->exchangeName, $this->fakeSubscriber); + + $this->publisher->publish($domainEvent); + + $this->consumer->consume( + $this->assertConsumer($domainEvent), + RabbitMqQueueNameFormatter::format($this->fakeSubscriber) + ); + + $this->assertTrue($this->consumerHasBeenExecuted); + } + + /** @test */ + public function it_should_retry_failed_domain_events(): void + { + $domainEvent = CourseCreatedDomainEventMother::create(); + + $this->configurer->configure($this->exchangeName, $this->fakeSubscriber); + + $this->publisher->publish($domainEvent); + + $this->simulateErrorConsuming(); + + sleep(1); + + $this->consumer->consume( + $this->assertConsumer($domainEvent), + RabbitMqQueueNameFormatter::format($this->fakeSubscriber) + ); + + $this->assertTrue($this->consumerHasBeenExecuted); + } + + /** @test */ + public function it_should_send_events_to_dead_letter_after_retry_failed_domain_events(): void + { + $domainEvent = CourseCreatedDomainEventMother::create(); + + $this->configurer->configure($this->exchangeName, $this->fakeSubscriber); + + $this->publisher->publish($domainEvent); + + $this->simulateErrorConsuming(); + + sleep(1); + + $this->simulateErrorConsuming(); + + $this->assertDeadLetterContainsEvent(1); + } + + protected function kernelClass(): string + { + return MoocBackendKernel::class; + } + + private function assertConsumer(DomainEvent ...$expectedDomainEvents): callable + { + return function (DomainEvent $domainEvent) use ($expectedDomainEvents): void { + $this->assertContainsEquals($domainEvent, $expectedDomainEvents); + + $this->consumerHasBeenExecuted = true; + }; + } + + private function failingConsumer(): callable + { + return static function (DomainEvent $domainEvent): never { + throw new RuntimeException('To test'); + }; + } + + private function simulateErrorConsuming(): void + { + try { + $this->consumer->consume($this->failingConsumer(), RabbitMqQueueNameFormatter::format($this->fakeSubscriber)); + } catch (Throwable $error) { + $this->assertInstanceOf(RuntimeException::class, $error); + } + } + + private function cleanEnvironment(RabbitMqConnection $connection): void + { + $connection->queue(RabbitMqQueueNameFormatter::format($this->fakeSubscriber))->delete(); + $connection->queue(RabbitMqQueueNameFormatter::formatRetry($this->fakeSubscriber))->delete(); + $connection->queue(RabbitMqQueueNameFormatter::formatDeadLetter($this->fakeSubscriber))->delete(); + } + + private function assertDeadLetterContainsEvent(int $expectedNumberOfEvents): void + { + $totalEventsInDeadLetter = 0; + + while ($this->connection->queue(RabbitMqQueueNameFormatter::formatDeadLetter($this->fakeSubscriber))->get( + AMQP_AUTOACK + )) { + $totalEventsInDeadLetter++; + } + + $this->assertSame($expectedNumberOfEvents, $totalEventsInDeadLetter); + } +} diff --git a/tests/Shared/Infrastructure/Bus/Event/RabbitMq/TestAllWorksOnRabbitMqEventsPublished.php b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/TestAllWorksOnRabbitMqEventsPublished.php new file mode 100644 index 000000000..937f28e08 --- /dev/null +++ b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/TestAllWorksOnRabbitMqEventsPublished.php @@ -0,0 +1,19 @@ +number; + } +} diff --git a/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php b/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php new file mode 100644 index 000000000..dc3f2095b --- /dev/null +++ b/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php @@ -0,0 +1,55 @@ +queryBus = new InMemorySymfonyQueryBus([$this->queryHandler()]); + } + + /** @test */ + public function it_should_return_a_response_successfully(): void + { + $this->expectException(RuntimeException::class); + + $this->queryBus->ask(new FakeQuery()); + } + + /** @test */ + public function it_should_raise_an_exception_dispatching_a_non_registered_query(): void + { + $this->expectException(QueryNotRegisteredError::class); + + $this->queryBus->ask($this->query()); + } + + private function queryHandler(): object + { + return new class() { + public function __invoke(FakeQuery $query): never + { + throw new RuntimeException('This works fine!'); + } + }; + } + + private function query(): MockInterface | Query + { + return $this->mock(Query::class); + } +} diff --git a/tests/Shared/Infrastructure/ConstantRandomNumberGenerator.php b/tests/Shared/Infrastructure/ConstantRandomNumberGenerator.php new file mode 100644 index 000000000..810e216cc --- /dev/null +++ b/tests/Shared/Infrastructure/ConstantRandomNumberGenerator.php @@ -0,0 +1,15 @@ +getConnection(); + + $tables = $this->tables($connection); + $truncateTablesSql = $this->truncateDatabaseSql($tables); + + $connection->executeQuery($truncateTablesSql); + } + + private function truncateDatabaseSql(array $tables): string + { + $truncateTables = map($this->truncateTableSql(), $tables); + + return sprintf('SET FOREIGN_KEY_CHECKS = 0; %s SET FOREIGN_KEY_CHECKS = 1;', implode(' ', $truncateTables)); + } + + private function truncateTableSql(): callable + { + return fn (array $table): string => sprintf('TRUNCATE TABLE `%s`;', (string) first($table)); + } + + private function tables(Connection $connection): array + { + return $connection->executeQuery('SHOW TABLES')->fetchAllAssociative(); + } +} diff --git a/tests/Shared/Infrastructure/Elastic/ElasticDatabaseCleaner.php b/tests/Shared/Infrastructure/Elastic/ElasticDatabaseCleaner.php new file mode 100644 index 000000000..293d5284a --- /dev/null +++ b/tests/Shared/Infrastructure/Elastic/ElasticDatabaseCleaner.php @@ -0,0 +1,27 @@ +client()->cat()->indices(); + + each( + static function (array $index) use ($client): void { + $indexName = $index['index']; + + $client->client()->indices()->delete(['index' => $indexName]); + $client->client()->indices()->create(['index' => $indexName]); + }, + $indices + ); + } +} diff --git a/tests/Shared/Infrastructure/Mink/MinkHelper.php b/tests/Shared/Infrastructure/Mink/MinkHelper.php new file mode 100644 index 000000000..ea443d6e1 --- /dev/null +++ b/tests/Shared/Infrastructure/Mink/MinkHelper.php @@ -0,0 +1,80 @@ + [], + 'files' => [], + 'server' => ['HTTP_ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json'], + 'content' => null, + 'changeHistory' => true, + ]; + + $optionalParams = array_merge($defaultOptionalParams, $optionalParams); + + $crawler = $this->getClient()->request( + $method, + $url, + $optionalParams['parameters'], + $optionalParams['files'], + $optionalParams['server'], + $optionalParams['content'], + $optionalParams['changeHistory'] + ); + + return $crawler; + } + + public function getResponse(): string + { + return $this->getSession()->getPage()->getContent(); + } + + public function getResponseHeaders(): array + { + return $this->normalizeHeaders(array_change_key_case($this->getSession()->getResponseHeaders(), CASE_LOWER)); + } + + public function resetServerParameters(): void + { + $this->getClient()->setServerParameters([]); + } + + public function getRequest(): object + { + return $this->getClient()->getRequest(); + } + + private function getSession(): Session + { + return $this->session; + } + + private function getDriver(): DriverInterface + { + return $this->getSession()->getDriver(); + } + + private function getClient(): AbstractBrowser + { + return $this->getDriver()->getClient(); + } + + private function normalizeHeaders(array $headers): array + { + return array_map('implode', array_filter($headers)); + } +} diff --git a/tests/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php b/tests/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php new file mode 100644 index 000000000..df914dd98 --- /dev/null +++ b/tests/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php @@ -0,0 +1,28 @@ +request($method, $url, $optionalParams); + } + + public function sendRequestWithPyStringNode($method, $url, PyStringNode $body): void + { + $this->request($method, $url, ['content' => $body->getRaw()]); + } + + public function request(string $method, string $url, array $optionalParams = []): Crawler + { + return $this->sessionHelper->sendRequest($method, $url, $optionalParams); + } +} diff --git a/tests/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php b/tests/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php new file mode 100644 index 000000000..1737b7adb --- /dev/null +++ b/tests/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php @@ -0,0 +1,29 @@ +constraint = new CodelyTvConstraintIsSimilar($value, $delta); + } + + public function match(&$actual): bool + { + return $this->constraint->evaluate($actual, '', true); + } + + public function __toString(): string + { + return 'Is similar'; + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootArraySimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootArraySimilarComparator.php new file mode 100644 index 000000000..22ee1fccf --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootArraySimilarComparator.php @@ -0,0 +1,49 @@ +contains($expected, $actual) || count($expected) !== count($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting the collection of AGs contains all the expected elements.' + ); + } + } + + private function contains(array $expectedArray, array $actualArray): bool + { + $exists = fn (AggregateRoot $expected): bool => any( + fn (AggregateRoot $actual): bool => TestUtils::isSimilar($expected, $actual), + $actualArray + ); + + return all($exists, $expectedArray); + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootSimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootSimilarComparator.php new file mode 100644 index 000000000..aefd86a36 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootSimilarComparator.php @@ -0,0 +1,74 @@ +pullDomainEvents(); + + if (!$this->aggregateRootsAreSimilar($expected, $actualEntity)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting the aggregate roots are equal.' + ); + } + } + + private function aggregateRootsAreSimilar(AggregateRoot $expected, AggregateRoot $actual): bool + { + if (!$this->aggregateRootsAreTheSameClass($expected, $actual)) { + return false; + } + + return $this->aggregateRootPropertiesAreSimilar($expected, $actual); + } + + private function aggregateRootsAreTheSameClass(AggregateRoot $expected, AggregateRoot $actual): bool + { + return $expected::class === $actual::class; + } + + private function aggregateRootPropertiesAreSimilar(AggregateRoot $expected, AggregateRoot $actual): bool + { + $expectedReflected = new ReflectionObject($expected); + $actualReflected = new ReflectionObject($actual); + + foreach ($expectedReflected->getProperties() as $expectedReflectedProperty) { + $actualReflectedProperty = $actualReflected->getProperty($expectedReflectedProperty->getName()); + + $expectedReflectedProperty->setAccessible(true); + $actualReflectedProperty->setAccessible(true); + + $expectedProperty = $expectedReflectedProperty->getValue($expected); + $actualProperty = $actualReflectedProperty->getValue($actual); + + if (!TestUtils::isSimilar($expectedProperty, $actualProperty)) { + return false; + } + } + + return true; + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeSimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeSimilarComparator.php new file mode 100644 index 000000000..67d3b5605 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeSimilarComparator.php @@ -0,0 +1,50 @@ +sub($intervalWithDelta) || $actual > $expectedUpper->add($intervalWithDelta)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->dateTimeToString($expected), + $this->dateTimeToString($actual), + false, + 'Failed asserting that two DateTime objects are equal.' + ); + } + } + + protected function dateTimeToString(DateTimeInterface $datetime): string + { + return $datetime->format(DateTime::ATOM) ?: 'Invalid DateTime object'; + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeStringSimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeStringSimilarComparator.php new file mode 100644 index 000000000..546c75cb9 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeStringSimilarComparator.php @@ -0,0 +1,71 @@ +isValidDateTimeString($expected) + && $this->isValidDateTimeString($actual); + } + + public function assertEquals( + $expected, + $actual, + $delta = 0.0, + $canonicalize = false, + $ignoreCase = false, + array &$processed = [] + ): void { + $expectedDate = new DateTimeImmutable($expected); + $actualDate = new DateTimeImmutable($actual); + + $normalizedDelta = $delta === 0.0 ? 10 : $delta; + $intervalWithDelta = new DateInterval(sprintf('PT%sS', abs($normalizedDelta))); + + if ($actualDate < $expectedDate->sub($intervalWithDelta) + || $actualDate > $expectedDate->add($intervalWithDelta)) { + throw new ComparisonFailure( + $expectedDate, + $actualDate, + $this->dateTimeToString($expectedDate), + $this->dateTimeToString($actualDate), + false, + 'Failed asserting that two DateTime strings are equal.' + ); + } + } + + protected function dateTimeToString(DateTimeInterface $datetime): string + { + $string = $datetime->format(DateTime::ATOM); + + return $string ?: 'Invalid DateTime object'; + } + + private function isValidDateTimeString(string $expected): bool + { + $isValid = true; + + try { + new DateTimeImmutable($expected); + } catch (Throwable) { + $isValid = false; + } + + return $isValid; + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php new file mode 100644 index 000000000..c191f7fb0 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php @@ -0,0 +1,49 @@ +contains($expected, $actual) || count($expected) !== count($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting the collection of Events contains all the expected elements.' + ); + } + } + + private function contains(array $expectedArray, array $actualArray): bool + { + $exists = static fn (DomainEvent $expected): bool => any( + static fn (DomainEvent $actual): bool => TestUtils::isSimilar($expected, $actual), + $actualArray + ); + + return all($exists, $expectedArray); + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventSimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventSimilarComparator.php new file mode 100644 index 000000000..f14cb6a77 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventSimilarComparator.php @@ -0,0 +1,75 @@ +areSimilar($expected, $actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting the events are equal.' + ); + } + } + + private function areSimilar(DomainEvent $expected, DomainEvent $actual): bool + { + if (!$this->areTheSameClass($expected, $actual)) { + return false; + } + + return $this->propertiesAreSimilar($expected, $actual); + } + + private function areTheSameClass(DomainEvent $expected, DomainEvent $actual): bool + { + return $expected::class === $actual::class; + } + + private function propertiesAreSimilar(DomainEvent $expected, DomainEvent $actual): bool + { + $expectedReflected = new ReflectionObject($expected); + $actualReflected = new ReflectionObject($actual); + + foreach ($expectedReflected->getProperties() as $expectedReflectedProperty) { + if (!in_array($expectedReflectedProperty->getName(), self::$ignoredAttributes, false)) { + $actualReflectedProperty = $actualReflected->getProperty($expectedReflectedProperty->getName()); + + $expectedReflectedProperty->setAccessible(true); + $actualReflectedProperty->setAccessible(true); + + $expectedProperty = $expectedReflectedProperty->getValue($expected); + $actualProperty = $actualReflectedProperty->getValue($actual); + + if (!TestUtils::isSimilar($expectedProperty, $actualProperty)) { + return false; + } + } + } + + return true; + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/Constraint/CodelyTvConstraintIsSimilar.php b/tests/Shared/Infrastructure/PhpUnit/Constraint/CodelyTvConstraintIsSimilar.php new file mode 100644 index 000000000..e5b4ca53a --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/Constraint/CodelyTvConstraintIsSimilar.php @@ -0,0 +1,75 @@ +value === $other) { + return true; + } + + $isValid = true; + $comparatorFactory = new Factory(); + + $comparatorFactory->register(new AggregateRootArraySimilarComparator()); + $comparatorFactory->register(new AggregateRootSimilarComparator()); + $comparatorFactory->register(new DomainEventArraySimilarComparator()); + $comparatorFactory->register(new DomainEventSimilarComparator()); + $comparatorFactory->register(new DateTimeSimilarComparator()); + $comparatorFactory->register(new DateTimeStringSimilarComparator()); + + try { + $comparator = $comparatorFactory->getComparatorFor($other, $this->value); + + $comparator->assertEquals($this->value, $other, $this->delta); + } catch (ComparisonFailure $f) { + if (!$returnResult) { + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + + $isValid = false; + } + + return $isValid; + } + + public function toString(): string + { + $delta = ''; + + if (is_string($this->value)) { + if (str_contains($this->value, "\n")) { + return 'is equal to '; + } + + return sprintf("is equal to '%s'", $this->value); + } + + if ($this->delta !== 0) { + $delta = sprintf(' with delta <%F>', $this->delta); + } + + return sprintf('is equal to %s%s', $this->exporter()->export($this->value), $delta); + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/InfrastructureTestCase.php b/tests/Shared/Infrastructure/PhpUnit/InfrastructureTestCase.php new file mode 100644 index 000000000..52438a8f0 --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/InfrastructureTestCase.php @@ -0,0 +1,64 @@ +kernelClass(); + + self::bootKernel(['environment' => 'test']); + + parent::setUp(); + } + + protected function assertSimilar(mixed $expected, mixed $actual): void + { + TestUtils::assertSimilar($expected, $actual); + } + + protected function service(string $id): mixed + { + return self::getContainer()->get($id); + } + + protected function parameter(string $parameter): mixed + { + return self::getContainer()->getParameter($parameter); + } + + protected function clearUnitOfWork(): void + { + $this->service(EntityManager::class)->clear(); + } + + /** @param int<0, max> $timeToWaitOnErrorInSeconds */ + protected function eventually( + callable $fn, + int $totalRetries = 3, + int $timeToWaitOnErrorInSeconds = 1, + int $attempt = 0 + ): void { + try { + $fn(); + } catch (Throwable $error) { + if ($totalRetries === $attempt) { + throw $error; + } + + sleep($timeToWaitOnErrorInSeconds); + + $this->eventually($fn, $totalRetries, $timeToWaitOnErrorInSeconds, $attempt + 1); + } + } +} diff --git a/tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php b/tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php new file mode 100644 index 000000000..599f2005d --- /dev/null +++ b/tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php @@ -0,0 +1,104 @@ +eventBus() + ->shouldReceive('publish') + ->with($this->similarTo($domainEvent)) + ->andReturnNull(); + } + + protected function shouldNotPublishDomainEvent(): void + { + $this->eventBus() + ->shouldReceive('publish') + ->withNoArgs() + ->andReturnNull(); + } + + protected function eventBus(): EventBus | MockInterface + { + return $this->eventBus ??= $this->mock(EventBus::class); + } + + protected function shouldGenerateUuid(string $uuid): void + { + $this->uuidGenerator() + ->shouldReceive('generate') + ->once() + ->withNoArgs() + ->andReturn($uuid); + } + + protected function uuidGenerator(): MockInterface | UuidGenerator + { + return $this->uuidGenerator ??= $this->mock(UuidGenerator::class); + } + + protected function notify(DomainEvent $event, callable $subscriber): void + { + $subscriber($event); + } + + protected function dispatch(Command $command, callable $commandHandler): void + { + $commandHandler($command); + } + + protected function assertAskResponse(Response $expected, Query $query, callable $queryHandler): void + { + $actual = $queryHandler($query); + + $this->assertEquals($expected, $actual); + } + + /** @param class-string $expectedErrorClass */ + protected function assertAskThrowsException(string $expectedErrorClass, Query $query, callable $queryHandler): void + { + $this->expectException($expectedErrorClass); + + $queryHandler($query); + } + + protected function isSimilar(mixed $expected, mixed $actual): bool + { + return TestUtils::isSimilar($expected, $actual); + } + + protected function assertSimilar(mixed $expected, mixed $actual): void + { + TestUtils::assertSimilar($expected, $actual); + } + + protected function similarTo(mixed $value, float $delta = 0.0): CodelyTvMatcherIsSimilar + { + return TestUtils::similarTo($value, $delta); + } +} diff --git a/tests/Shared/SharedArchitectureTest.php b/tests/Shared/SharedArchitectureTest.php new file mode 100644 index 000000000..da8b1e751 --- /dev/null +++ b/tests/Shared/SharedArchitectureTest.php @@ -0,0 +1,62 @@ +classes(Selector::inNamespace('CodelyTv\Shared\Domain')) + ->canOnlyDependOn() + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ + // Itself + Selector::inNamespace('CodelyTv\Shared\Domain'), + // Dependencies treated as domain + Selector::classname(Uuid::class), + ])) + ->because('shared domain cannot import from outside'); + } + + public function test_shared_infrastructure_should_not_import_from_other_contexts(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('CodelyTv\Shared\Infrastructure')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('CodelyTv')) + ->excluding( + // Itself + Selector::inNamespace('CodelyTv\Shared'), + // This need to be refactored + Selector::classname(MySqlDatabaseCleaner::class), + Selector::classname(AuthenticateUserCommand::class), + Selector::inNamespace('CodelyTv\Backoffice\Auth'), + ); + } + + public function test_all_use_cases_can_only_have_one_public_method(): Rule + { + return PHPat::rule() + ->classes( + Selector::classname('/^CodelyTv\\\\.+\\\\.+\\\\Application\\\\.+\\\\(?!.*(?:Command|Query)$).*$/', true) + ) + ->excluding( + Selector::implements(Response::class), + Selector::implements(DomainEventSubscriber::class), + Selector::inNamespace('/.*\\\\Tests\\\\.*/', true) + ) + ->shouldHaveOnlyOnePublicMethod(); + } +} diff --git a/tests/applications/mooc_backend/features/student/student_get.feature b/tests/applications/mooc_backend/features/student/student_get.feature deleted file mode 100644 index b77cc038c..000000000 --- a/tests/applications/mooc_backend/features/student/student_get.feature +++ /dev/null @@ -1,33 +0,0 @@ -Feature: Find a student - In order to learn from CodelyTV Pro courses - As an authenticated student - I want to view my profile details - - Background: - Given there is an student: - | id | fe7017d0-9e8f-4952-99d1-e047e36b1694 | - | total_pending_videos | 5 | - | name | VicenΓ§ | - - Scenario: Find an existing student - Given I send a GET request to "/students/fe7017d0-9e8f-4952-99d1-e047e36b1694" - Then the response status code should be 200 - And the response content should be: - """ - { - "id": "fe7017d0-9e8f-4952-99d1-e047e36b1694", - "name": "VicenΓ§", - "total_pending_videos": 5 - } - """ - - Scenario: Not find a non existing video - Given I send a GET request to "/students/05ed7eb7-7888-4730-b9e9-0c16cfa80b80" - Then the response status code should be 404 - And the response content should be: - """ - { - "code": "student_not_exist", - "message": "The student <05ed7eb7-7888-4730-b9e9-0c16cfa80b80> does not exists" - } - """ diff --git a/tests/applications/mooc_backend/features/student/student_put.feature b/tests/applications/mooc_backend/features/student/student_put.feature deleted file mode 100644 index 0d3cb770a..000000000 --- a/tests/applications/mooc_backend/features/student/student_put.feature +++ /dev/null @@ -1,14 +0,0 @@ -Feature: Sign up a student - In order to learn from CodelyTV Pro courses - As an anonymous student - I want to sign up to the platform - - Scenario: Sign up a new student - Given I send a PUT request to "/students/0ca24fc4-bdc8-48d0-9c5f-94183a627adc" with body: - """ - { - "name": "javi" - } - """ - Then the response status code should be 201 - And the response should be empty diff --git a/tests/applications/mooc_backend/features/video/video_get.feature b/tests/applications/mooc_backend/features/video/video_get.feature deleted file mode 100644 index deef80c60..000000000 --- a/tests/applications/mooc_backend/features/video/video_get.feature +++ /dev/null @@ -1,44 +0,0 @@ -Feature: Find a video - In order to be the best youtuber ever - As a codelyver - I want to find a video - - Background: - Given I send a POST request to "/video" with body: - """ - { - "request_id": "170cfccd-869d-414b-a521-9cce9e0e67a2", - "id": "465892a1-5a77-4cee-9450-46ecd6b68f69", - "title": "Exprimiendo los tipos de PHP7", - "url": "https://codely.tv/screencasts/tipos-php-7/", - "type": "screencast", - "course_id": "9c8a481a-0fe2-49cf-ab8a-79bcc2965d00" - } - """ - Then the response should be empty - And the response status code should be 201 - - Scenario: Find an existing video - Given I send a GET request to "/video/465892a1-5a77-4cee-9450-46ecd6b68f69" - Then the response status code should be 200 - And the response content should be: - """ - { - "id": "465892a1-5a77-4cee-9450-46ecd6b68f69", - "title": "Exprimiendo los tipos de PHP7", - "url": "https://codely.tv/screencasts/tipos-php-7/", - "type": "screencast", - "course_id": "9c8a481a-0fe2-49cf-ab8a-79bcc2965d00" - } - """ - - Scenario: Not find a non existing video - Given I send a GET request to "/video/09acb178-0831-4d86-a364-bff0e19d8f19" - Then the response status code should be 404 - And the response content should be: - """ - { - "code": "video_not_found", - "message": "The video <09acb178-0831-4d86-a364-bff0e19d8f19> has not been found" - } - """ diff --git a/tests/applications/mooc_backend/features/video/video_post.feature b/tests/applications/mooc_backend/features/video/video_post.feature deleted file mode 100644 index f0d1b3319..000000000 --- a/tests/applications/mooc_backend/features/video/video_post.feature +++ /dev/null @@ -1,34 +0,0 @@ -Feature: Create video - In order to be the best youtuber ever - As a codelyver - I want to create a video - - Scenario: Create an screencast video - Given I send a POST request to "/video" with body: - """ - { - "request_id": "170cfccd-869d-414b-a521-9cce9e0e67a2", - "id": "465892a1-5a77-4cee-9450-46ecd6b68f69", - "title": "Exprimiendo los tipos de PHP7", - "url": "https://codely.tv/screencasts/tipos-php-7/", - "type": "screencast", - "course_id": "9c8a481a-0fe2-49cf-ab8a-79bcc2965d00" - } - """ - Then the response should be empty - And the response status code should be 201 - - Scenario: Create an interview video - Given I send a POST request to "/video" with body: - """ - { - "request_id": "6ee07c6b-a1e7-4cfa-abf5-cbfd4884cd75", - "id": "87cf6e20-a79b-4f0e-bf26-4a69de0cafe1", - "title": "Entrevista RaΓΊl Raja - CTO 47 Degrees", - "url": "https://codely.tv/entrevistas/raul-raja-cto-47-degrees/", - "type": "interview", - "course_id": "48c2c2ea-bd93-4248-9f71-81ad37ad5647" - } - """ - Then the response should be empty - And the response status code should be 201 diff --git a/tests/applications/mooc_backend/mooc_backend.yml b/tests/applications/mooc_backend/mooc_backend.yml deleted file mode 100644 index fb3755e70..000000000 --- a/tests/applications/mooc_backend/mooc_backend.yml +++ /dev/null @@ -1,31 +0,0 @@ -mooc_backend: - extensions: - FriendsOfBehat\SymfonyExtension: - kernel: - class: CodelyTv\MoocBackend\MoocBackendKernel - Behat\MinkExtension: - sessions: - symfony: - symfony: ~ - base_url: 'http://localhost' - - suites: - status: - paths: [ tests/applications/mooc_backend/features/status ] - contexts: - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiFeatureContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiRequestContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiResponseContext - video: - paths: [ tests/applications/mooc_backend/features/video ] - contexts: - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiFeatureContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiRequestContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiResponseContext - student: - paths: [ tests/applications/mooc_backend/features/student ] - contexts: - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiFeatureContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiRequestContext - - CodelyTv\Test\Shared\Infrastructure\Behat\ApiContext\ApiResponseContext - - CodelyTv\Test\Mooc\Students\StudentModuleBehatContext diff --git a/tests/src/Backoffice/README.md b/tests/src/Backoffice/README.md deleted file mode 100644 index c639cd6d2..000000000 --- a/tests/src/Backoffice/README.md +++ /dev/null @@ -1 +0,0 @@ -Here we'll have our Backoffice tests :) diff --git a/tests/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreatedTest.php b/tests/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreatedTest.php deleted file mode 100644 index 6097f4814..000000000 --- a/tests/src/Mooc/Notifications/Application/Create/CreateNotificationOnVideoCreatedTest.php +++ /dev/null @@ -1,20 +0,0 @@ -service('codely.mooc.notifications.repository'); - } -} diff --git a/tests/src/Mooc/Notifications/NotificationModuleUnitTestCase.php b/tests/src/Mooc/Notifications/NotificationModuleUnitTestCase.php deleted file mode 100644 index 0eb2e8564..000000000 --- a/tests/src/Mooc/Notifications/NotificationModuleUnitTestCase.php +++ /dev/null @@ -1,20 +0,0 @@ -repository = $this->repository ?: $this->mock(NotificationRepository::class); - } -} diff --git a/tests/src/Mooc/Shared/Domain/Courses/CourseIdMother.php b/tests/src/Mooc/Shared/Domain/Courses/CourseIdMother.php deleted file mode 100644 index b1e226850..000000000 --- a/tests/src/Mooc/Shared/Domain/Courses/CourseIdMother.php +++ /dev/null @@ -1,21 +0,0 @@ -service(MoocEnvironmentArranger::class), - ]; - } -} diff --git a/tests/src/Mooc/Shared/Infrastructure/MoocContextUnitTestCase.php b/tests/src/Mooc/Shared/Infrastructure/MoocContextUnitTestCase.php deleted file mode 100644 index 65fbfec45..000000000 --- a/tests/src/Mooc/Shared/Infrastructure/MoocContextUnitTestCase.php +++ /dev/null @@ -1,11 +0,0 @@ -entityManager = $entityManager; - } - - public function arrange(): void - { - apply(new DatabaseCleaner(), [$this->entityManager]); - } - - public function close(): void - { - } -} diff --git a/tests/src/Mooc/Steps/Domain/Challenge/ChallengeStepMother.php b/tests/src/Mooc/Steps/Domain/Challenge/ChallengeStepMother.php deleted file mode 100644 index aa9a37aa3..000000000 --- a/tests/src/Mooc/Steps/Domain/Challenge/ChallengeStepMother.php +++ /dev/null @@ -1,48 +0,0 @@ -repository()->save($step); - } - - public function validSteps(): array - { - return [ - ['challenge step' => ChallengeStepMother::random()], - ['quiz step' => QuizStepMother::random()], - ['video step' => VideoStepMother::random()], - ]; - } - - private function repository(): StepRepository - { - return $this->service(StepRepositoryMySql::class); - } -} diff --git a/tests/src/Mooc/Steps/StepsModuleFunctionalTestCase.php b/tests/src/Mooc/Steps/StepsModuleFunctionalTestCase.php deleted file mode 100644 index 2e276d771..000000000 --- a/tests/src/Mooc/Steps/StepsModuleFunctionalTestCase.php +++ /dev/null @@ -1,11 +0,0 @@ -value()); - } - - public static function random(): FindStudentQuery - { - return self::create(StudentIdMother::random()); - } -} diff --git a/tests/src/Mooc/Students/Application/Find/FindStudentTest.php b/tests/src/Mooc/Students/Application/Find/FindStudentTest.php deleted file mode 100644 index 3b04f1e2f..000000000 --- a/tests/src/Mooc/Students/Application/Find/FindStudentTest.php +++ /dev/null @@ -1,55 +0,0 @@ -repository()); - - $this->handler = new FindStudentQueryHandler($finder); - } - - /** @test */ - public function it_should_find_an_existing_student(): void - { - $query = FindStudentQueryMother::random(); - - $id = StudentIdMother::create($query->id()); - $student = StudentMother::withId($id); - - $response = StudentResponseMother::create($student->id(), $student->name(), $student->totalVideosCreated()); - - $this->shouldSearchStudent($id, $student); - - $this->assertAskResponse($query, $response, $this->handler); - } - - /** @test */ - public function it_should_throw_an_exception_finding_a_non_existing_student(): void - { - $query = FindStudentQueryMother::random(); - - $id = StudentIdMother::create($query->id()); - - $this->shouldSearchStudent($id); - - $this->assertAskThrowsException(StudentNotExist::class, $query, $this->handler); - } -} diff --git a/tests/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentPendingVideosOnVideoPublishedTest.php b/tests/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentPendingVideosOnVideoPublishedTest.php deleted file mode 100644 index 441d3db0c..000000000 --- a/tests/src/Mooc/Students/Application/IncreasePendingVideos/IncreaseStudentPendingVideosOnVideoPublishedTest.php +++ /dev/null @@ -1,52 +0,0 @@ -repository()); - - $this->subscriber = new IncreaseStudentTotalVideosCreatedOnVideoCreated($increaser); - } - - /** @test */ - public function it_should_increase_student_total_videos_created_on_scala_video_created(): void - { - $event = ScalaVideoCreatedDomainEventMother::random(); - - $id = StudentIdMother::create($event->creatorId()); - $student = StudentMother::withId($id); - - $updatedStudent = DuplicatorMother::with( - $student, - [ - 'totalVideosCreated' => StudentTotalVideosCreatedMother::create( - $student->totalVideosCreated()->value() + 1 - ), - ] - ); - - $this->shouldSearchStudent($id, $student); - $this->shouldSaveStudent($updatedStudent); - - $this->notify($event, $this->subscriber); - } -} diff --git a/tests/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEventMother.php b/tests/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEventMother.php deleted file mode 100644 index 7e8989f9e..000000000 --- a/tests/src/Mooc/Students/Domain/ScalaVideoCreatedDomainEventMother.php +++ /dev/null @@ -1,23 +0,0 @@ -value(), ['creatorId' => $userId->value()]); - } - - public static function random(): ScalaVideoCreatedDomainEvent - { - return self::create(VideoIdMother::random(), StudentIdMother::random()); - } -} diff --git a/tests/src/Mooc/Students/Domain/StudentIdMother.php b/tests/src/Mooc/Students/Domain/StudentIdMother.php deleted file mode 100644 index fcfcddf52..000000000 --- a/tests/src/Mooc/Students/Domain/StudentIdMother.php +++ /dev/null @@ -1,21 +0,0 @@ -value(), $name->value(), $totalPendingVideos->value()); - } - - public static function random(): StudentResponse - { - return self::create( - StudentIdMother::random(), - StudentNameMother::random(), - StudentTotalVideosCreatedMother::random() - ); - } -} diff --git a/tests/src/Mooc/Students/Domain/StudentTotalVideosCreatedMother.php b/tests/src/Mooc/Students/Domain/StudentTotalVideosCreatedMother.php deleted file mode 100644 index fc43e7344..000000000 --- a/tests/src/Mooc/Students/Domain/StudentTotalVideosCreatedMother.php +++ /dev/null @@ -1,21 +0,0 @@ -repository()->save(StudentMother::random()); - } - - /** @test */ - public function it_should_find_an_existing_video(): void - { - $student = StudentMother::random(); - - $this->repository()->save($student); - $this->clearUnitOfWork(); - - $this->assertSimilar($student, $this->repository()->search($student->id())); - } - - /** @test */ - public function it_should_find_multiples_video(): void - { - $student = StudentMother::random(); - $another = StudentMother::random(); - $students = StudentsMother::create($student, $another); - - $this->repository()->saveAll($students); - $this->clearUnitOfWork(); - - $this->assertSimilar($students, $this->repository()->all()); - } - - /** @test */ - public function it_should_not_find_a_non_existing_video(): void - { - $this->assertNull($this->repository()->search(StudentIdMother::random())); - } - - private function repository(): StudentRepository - { - return $this->service(StudentRepositoryMySql::class); - } -} diff --git a/tests/src/Mooc/Students/StudentModuleBehatContext.php b/tests/src/Mooc/Students/StudentModuleBehatContext.php deleted file mode 100644 index 311bf45eb..000000000 --- a/tests/src/Mooc/Students/StudentModuleBehatContext.php +++ /dev/null @@ -1,38 +0,0 @@ -repository = $repository; - } - - /** - * @Given /^there is an student:$/ - */ - public function thereIsAnStudent(TableNode $table): void - { - apply($this->creator(), [$table->getRowsHash()]); - } - - private function creator(): callable - { - return function (array $student) { - $this->repository->save( - StudentMother::withValues($student['id'], $student['name'], (int) $student['total_pending_videos']) - ); - }; - } -} diff --git a/tests/src/Mooc/Students/StudentModuleFunctionalTestCase.php b/tests/src/Mooc/Students/StudentModuleFunctionalTestCase.php deleted file mode 100644 index 0295e29bb..000000000 --- a/tests/src/Mooc/Students/StudentModuleFunctionalTestCase.php +++ /dev/null @@ -1,11 +0,0 @@ -repository = $this->repository ?: $this->mock(StudentRepository::class); - } - - protected function shouldSaveStudent(Student $student): void - { - $this->repository() - ->shouldReceive('save') - ->with(similarTo($student)) - ->once() - ->andReturnNull(); - } - - protected function shouldSearchStudent(StudentId $id, Student $student = null): void - { - $this->repository() - ->shouldReceive('search') - ->with(equalTo($id)) - ->once() - ->andReturn($student); - } -} diff --git a/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandMother.php b/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandMother.php deleted file mode 100644 index 80412eb9e..000000000 --- a/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentCommandMother.php +++ /dev/null @@ -1,37 +0,0 @@ -value(), $videoId->value(), $content->value()); - } - - public static function random(): PublishVideoCommentCommand - { - return self::create( - new Uuid(UuidMother::random()), - VideoCommentIdMother::random(), - VideoIdMother::random(), - VideoCommentContentMother::random() - ); - } -} diff --git a/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentTest.php b/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentTest.php deleted file mode 100644 index 4a8807a69..000000000 --- a/tests/src/Mooc/VideoComments/Application/Publish/PublishVideoCommentTest.php +++ /dev/null @@ -1,85 +0,0 @@ -repository(), $this->queryBus(), $this->domainEventPublisher()); - - $this->handler = new PublishVideoCommentCommandHandler($publisher); - } - - /** @test */ - public function it_should_publish_a_video_comment(): void - { - $command = PublishVideoCommentCommandMother::random(); - - $id = VideoCommentIdMother::create($command->id()); - $videoId = VideoIdMother::create($command->videoId()); - $content = VideoCommentContentMother::create($command->content()); - - $comment = VideoCommentMother::create($id, $videoId, $content); - - $domainEvent = VideoCommentPublishedDomainEventMother::create($id, $videoId, $content); - - $this->shouldAsk(FindVideoQueryMother::create($videoId), VideoResponseMother::withId($videoId)); - $this->shouldSaveVideoComment($comment); - $this->shouldPublishDomainEvents($domainEvent); - - $this->dispatch($command, $this->handler); - } - - /** @test */ - public function it_should_not_publish_a_video_comment_when_the_video_not_exist(): void - { - $this->expectException(VideoNotFound::class); - - $command = PublishVideoCommentCommandMother::random(); - - $videoId = VideoIdMother::create($command->videoId()); - - $this->shouldAskThrowingException(FindVideoQueryMother::create($videoId), new VideoNotFound($videoId)); - - $this->dispatch($command, $this->handler); - } - - /** @return VideoCommentRepository|MockInterface */ - private function repository() - { - return $this->repository = $this->repository ?: $this->mock(VideoCommentRepository::class); - } - - private function shouldSaveVideoComment(VideoComment $comment): void - { - $this->repository() - ->shouldReceive('save') - ->once() - ->with(similarTo($comment)) - ->andReturnNull(); - } -} diff --git a/tests/src/Mooc/VideoComments/Domain/VideoCommentContentMother.php b/tests/src/Mooc/VideoComments/Domain/VideoCommentContentMother.php deleted file mode 100644 index b72d9f1b6..000000000 --- a/tests/src/Mooc/VideoComments/Domain/VideoCommentContentMother.php +++ /dev/null @@ -1,23 +0,0 @@ -value(), - [ - 'videoId' => $videoId->value(), - 'content' => $content->value(), - ] - ); - } - - public static function random(): VideoCommentPublishedDomainEvent - { - return self::create( - VideoCommentIdMother::random(), - VideoIdMother::random(), - VideoCommentContentMother::random() - ); - } -} diff --git a/tests/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandlerTest.php b/tests/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandlerTest.php deleted file mode 100644 index d900c766c..000000000 --- a/tests/src/Mooc/VideoHighlights/Application/Create/CreateVideoHighlightCommandHandlerTest.php +++ /dev/null @@ -1,20 +0,0 @@ -service('codely.mooc.video_highlights.repository'); - } -} diff --git a/tests/src/Mooc/VideoHighlights/VideoHighlightModuleUnitTestCase.php b/tests/src/Mooc/VideoHighlights/VideoHighlightModuleUnitTestCase.php deleted file mode 100644 index 55b85d154..000000000 --- a/tests/src/Mooc/VideoHighlights/VideoHighlightModuleUnitTestCase.php +++ /dev/null @@ -1,20 +0,0 @@ -repository = $this->repository ?: $this->mock(VideoHighlightRepository::class); - } -} diff --git a/tests/src/Mooc/Videos/Application/Create/CreateVideoCommandMother.php b/tests/src/Mooc/Videos/Application/Create/CreateVideoCommandMother.php deleted file mode 100644 index 433a97c74..000000000 --- a/tests/src/Mooc/Videos/Application/Create/CreateVideoCommandMother.php +++ /dev/null @@ -1,52 +0,0 @@ -value(), - $type->value(), - $title->value(), - $url->value(), - $courseId->value() - ); - } - - public static function random(): CreateVideoCommand - { - return self::create( - new Uuid(UuidMother::random()), - VideoIdMother::random(), - VideoTypeMother::random(), - VideoTitleMother::random(), - VideoUrlMother::random(), - CourseIdMother::random() - ); - } -} diff --git a/tests/src/Mooc/Videos/Application/Create/CreateVideoTest.php b/tests/src/Mooc/Videos/Application/Create/CreateVideoTest.php deleted file mode 100644 index bf093bb8b..000000000 --- a/tests/src/Mooc/Videos/Application/Create/CreateVideoTest.php +++ /dev/null @@ -1,52 +0,0 @@ -repository(), $this->domainEventPublisher()); - - $this->handler = new CreateVideoCommandHandler($creator); - } - - /** @test */ - public function it_should_create_a_video(): void - { - $command = CreateVideoCommandMother::random(); - - $id = VideoIdMother::create($command->id()); - $type = VideoTypeMother::create($command->type()); - $title = VideoTitleMother::create($command->title()); - $url = VideoUrlMother::create($command->url()); - $courseId = CourseIdMother::create($command->courseId()); - - $video = VideoMother::create($id, $type, $title, $url, $courseId); - - $domainEvent = VideoCreatedDomainEventMother::create($id, $type, $title, $url, $courseId); - - $this->shouldSaveVideo($video); - $this->shouldPublishDomainEvents($domainEvent); - - $this->dispatch($command, $this->handler); - } -} diff --git a/tests/src/Mooc/Videos/Application/Find/FindVideoQueryMother.php b/tests/src/Mooc/Videos/Application/Find/FindVideoQueryMother.php deleted file mode 100644 index 663d6b397..000000000 --- a/tests/src/Mooc/Videos/Application/Find/FindVideoQueryMother.php +++ /dev/null @@ -1,22 +0,0 @@ -value()); - } - - public static function random(): FindVideoQuery - { - return self::create(VideoIdMother::random()); - } -} diff --git a/tests/src/Mooc/Videos/Application/Find/FindVideoTest.php b/tests/src/Mooc/Videos/Application/Find/FindVideoTest.php deleted file mode 100644 index d4062a997..000000000 --- a/tests/src/Mooc/Videos/Application/Find/FindVideoTest.php +++ /dev/null @@ -1,60 +0,0 @@ -repository()); - - $this->handler = new FindVideoQueryHandler($finder); - } - - /** @test */ - public function it_should_find_an_existing_video(): void - { - $query = FindVideoQueryMother::random(); - - $id = VideoIdMother::create($query->id()); - $video = VideoMother::withId($id); - - $response = VideoResponseMother::create( - $video->id(), - $video->type(), - $video->title(), - $video->url(), - $video->courseId() - ); - - $this->shouldSearchVideo($id, $video); - - $this->assertAskResponse($query, $response, $this->handler); - } - - /** @test */ - public function it_should_throw_an_exception_finding_a_non_existing_video(): void - { - $query = FindVideoQueryMother::random(); - - $id = VideoIdMother::create($query->id()); - - $this->shouldSearchVideo($id); - - $this->assertAskThrowsException(VideoNotFound::class, $query, $this->handler); - } -} diff --git a/tests/src/Mooc/Videos/Application/Find/VideoResponseMother.php b/tests/src/Mooc/Videos/Application/Find/VideoResponseMother.php deleted file mode 100644 index f0fc13599..000000000 --- a/tests/src/Mooc/Videos/Application/Find/VideoResponseMother.php +++ /dev/null @@ -1,47 +0,0 @@ -value(), $type->value(), $title->value(), $url->value(), $courseId->value()); - } - - public static function withId(VideoId $id): VideoResponse - { - return DuplicatorMother::with(self::random(), ['id' => $id->value()]); - } - - public static function random(): VideoResponse - { - return self::create( - VideoIdMother::random(), - VideoTypeMother::random(), - VideoTitleMother::random(), - VideoUrlMother::random(), - CourseIdMother::random() - ); - } -} diff --git a/tests/src/Mooc/Videos/Domain/VideoCreatedDomainEventMother.php b/tests/src/Mooc/Videos/Domain/VideoCreatedDomainEventMother.php deleted file mode 100644 index 7e6891689..000000000 --- a/tests/src/Mooc/Videos/Domain/VideoCreatedDomainEventMother.php +++ /dev/null @@ -1,46 +0,0 @@ -value(), - [ - 'type' => $type->value(), - 'title' => $title->value(), - 'url' => $url->value(), - 'courseId' => $courseId->value(), - ] - ); - } - - public static function random(): VideoCreatedDomainEvent - { - return self::create( - VideoIdMother::random(), - VideoTypeMother::random(), - VideoTitleMother::random(), - VideoUrlMother::random(), - CourseIdMother::random() - ); - } -} diff --git a/tests/src/Mooc/Videos/Domain/VideoIdMother.php b/tests/src/Mooc/Videos/Domain/VideoIdMother.php deleted file mode 100644 index 511ec8f91..000000000 --- a/tests/src/Mooc/Videos/Domain/VideoIdMother.php +++ /dev/null @@ -1,21 +0,0 @@ -repository()->save(VideoMother::random()); - } - - /** @test */ - public function it_should_find_an_existing_video(): void - { - $video = VideoMother::random(); - - $this->repository()->save($video); - $this->clearUnitOfWork(); - - $this->assertSimilar($video, $this->repository()->search($video->id())); - } - - /** @test */ - public function it_should_not_find_a_non_existing_video(): void - { - $this->assertNull($this->repository()->search(VideoIdMother::random())); - } - - private function repository(): VideoRepository - { - return $this->service(VideoRepositoryMySql::class); - } -} diff --git a/tests/src/Mooc/Videos/VideoModuleFunctionalTestCase.php b/tests/src/Mooc/Videos/VideoModuleFunctionalTestCase.php deleted file mode 100644 index fb4ea05c0..000000000 --- a/tests/src/Mooc/Videos/VideoModuleFunctionalTestCase.php +++ /dev/null @@ -1,11 +0,0 @@ -repository = $this->repository ?: $this->mock(VideoRepository::class); - } - - protected function shouldSaveVideo(Video $video): void - { - $this->repository() - ->shouldReceive('save') - ->with(similarTo($video)) - ->once() - ->andReturnNull(); - } - - protected function shouldSearchVideo(VideoId $id, Video $video = null): void - { - $this->repository() - ->shouldReceive('search') - ->with(equalTo($id)) - ->once() - ->andReturn($video); - } -} diff --git a/tests/src/Shared/Domain/BoolMother.php b/tests/src/Shared/Domain/BoolMother.php deleted file mode 100644 index 987a86d86..000000000 --- a/tests/src/Shared/Domain/BoolMother.php +++ /dev/null @@ -1,13 +0,0 @@ -boolean; - } -} diff --git a/tests/src/Shared/Domain/Criteria/CriteriaMother.php b/tests/src/Shared/Domain/Criteria/CriteriaMother.php deleted file mode 100644 index e90912fe0..000000000 --- a/tests/src/Shared/Domain/Criteria/CriteriaMother.php +++ /dev/null @@ -1,33 +0,0 @@ -dateTimeBetween('-1 year')); - } - - public static function now(): DateTimeImmutable - { - return self::create('now'); - } - - public static function aLongTimeAgo(): DateTimeImmutable - { - return static::immutable(MotherCreator::random()->dateTimeBetween('-2 year', '-1 year')); - } - - public static function random(): DateTimeImmutable - { - return new DateTimeImmutable(); - } - - private static function immutable(DateTime $date): DateTimeImmutable - { - return DateTimeImmutable::createFromMutable($date); - } -} diff --git a/tests/src/Shared/Domain/DateTimeZoneMother.php b/tests/src/Shared/Domain/DateTimeZoneMother.php deleted file mode 100644 index b0fbf4683..000000000 --- a/tests/src/Shared/Domain/DateTimeZoneMother.php +++ /dev/null @@ -1,25 +0,0 @@ -timezone); - } - - public static function UTC(): DateTimeZone - { - return self::create('UTC'); - } -} diff --git a/tests/src/Shared/Domain/DateTimestampMother.php b/tests/src/Shared/Domain/DateTimestampMother.php deleted file mode 100644 index 73c369020..000000000 --- a/tests/src/Shared/Domain/DateTimestampMother.php +++ /dev/null @@ -1,21 +0,0 @@ -getName()])) { - $property->setAccessible(true); - $property->setValue($duplicated, $newParams[$property->getName()]); - } - }, - $reflection->getProperties() - ); - - return $duplicated; - } -} diff --git a/tests/src/Shared/Domain/ImageUrlMother.php b/tests/src/Shared/Domain/ImageUrlMother.php deleted file mode 100644 index 5b7eb3e56..000000000 --- a/tests/src/Shared/Domain/ImageUrlMother.php +++ /dev/null @@ -1,13 +0,0 @@ -imageUrl(); - } -} diff --git a/tests/src/Shared/Domain/Md5Mother.php b/tests/src/Shared/Domain/Md5Mother.php deleted file mode 100644 index 37bc10539..000000000 --- a/tests/src/Shared/Domain/Md5Mother.php +++ /dev/null @@ -1,13 +0,0 @@ -md5; - } -} diff --git a/tests/src/Shared/Domain/MotherCreator.php b/tests/src/Shared/Domain/MotherCreator.php deleted file mode 100644 index eb0683881..000000000 --- a/tests/src/Shared/Domain/MotherCreator.php +++ /dev/null @@ -1,23 +0,0 @@ -numberBetween($min, $max); - } - - public static function lessThan($max): int - { - return self::between(1, $max); - } - - public static function moreThan($min): int - { - return self::between($min); - } - - public static function float($numDecimals = null): float - { - return MotherCreator::random()->randomFloat($numDecimals); - } - - public static function floatBetween($min, $max, $numDecimals = null): float - { - return MotherCreator::random()->randomFloat($numDecimals, $min, $max); - } - - public static function randomPercentage(): int - { - return self::between(0, 100); - } - - public static function randomPositive(): int - { - return self::between(0, 1000); - } - - public static function random(): int - { - return self::between(1); - } -} diff --git a/tests/src/Shared/Domain/RandomElementMother.php b/tests/src/Shared/Domain/RandomElementMother.php deleted file mode 100644 index 60efb3fcf..000000000 --- a/tests/src/Shared/Domain/RandomElementMother.php +++ /dev/null @@ -1,13 +0,0 @@ -randomElement($choices); - } -} diff --git a/tests/src/Shared/Domain/RepeatMother.php b/tests/src/Shared/Domain/RepeatMother.php deleted file mode 100644 index d367a17dc..000000000 --- a/tests/src/Shared/Domain/RepeatMother.php +++ /dev/null @@ -1,25 +0,0 @@ -text($maxNumbersOfCharacters); - } - - public static function short(): string - { - return self::create(100); - } - - public static function random(): string - { - return self::create(NumberMother::between(100, 2000)); - } - - public static function withMinLength(int $minLength): string - { - $numWords = $minLength; - $variableNumWords = false; - - return MotherCreator::random()->sentence($numWords, $variableNumWords); - } -} diff --git a/tests/src/Shared/Domain/UrlMother.php b/tests/src/Shared/Domain/UrlMother.php deleted file mode 100644 index 622dca103..000000000 --- a/tests/src/Shared/Domain/UrlMother.php +++ /dev/null @@ -1,13 +0,0 @@ -url; - } -} diff --git a/tests/src/Shared/Domain/UuidMother.php b/tests/src/Shared/Domain/UuidMother.php deleted file mode 100644 index b508a04b6..000000000 --- a/tests/src/Shared/Domain/UuidMother.php +++ /dev/null @@ -1,13 +0,0 @@ -unique()->uuid; - } -} diff --git a/tests/src/Shared/Domain/WordMother.php b/tests/src/Shared/Domain/WordMother.php deleted file mode 100644 index e0ff29ee1..000000000 --- a/tests/src/Shared/Domain/WordMother.php +++ /dev/null @@ -1,13 +0,0 @@ -word; - } -} diff --git a/tests/src/Shared/Enum/EnumTest.php b/tests/src/Shared/Enum/EnumTest.php deleted file mode 100644 index 138ff83d9..000000000 --- a/tests/src/Shared/Enum/EnumTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertEquals('one', StringTestEnum::one()->value()); - $this->assertEquals('two', StringTestEnum::two()->value()); - $this->assertEquals('A very large number', StringTestEnum::aVeryLargeNumber()->value()); - } - - /** @test */ - public function it_should_be_able_to_construct_enums_with_numbers_inside(): void - { - $this->assertEquals(1, NumberTestEnum::one()->value()); - $this->assertEquals(2, NumberTestEnum::two()->value()); - } -} diff --git a/tests/src/Shared/Enum/NumberTestEnum.php b/tests/src/Shared/Enum/NumberTestEnum.php deleted file mode 100644 index 7e1d4cab2..000000000 --- a/tests/src/Shared/Enum/NumberTestEnum.php +++ /dev/null @@ -1,22 +0,0 @@ -minkSession = $minkSession; - } - - - /** - * @Given I send a :method request to :url - */ - public function iSendARequestTo($method, $url): void - { - $this->getSessionRequestHelper()->sendRequest($method, $this->locatePath($url)); - } - - /** - * @When I send a :method request to :url with the parameters: - */ - public function iSendARequestToWithParameters($method, $url, TableNode $parameters): void - { - $this->getSessionRequestHelper()->sendRequestWithTableNode($method, $this->locatePath($url), $parameters); - } - - /** - * @Given I send a :method request to :url with body: - */ - public function iSendARequestToWithBody($method, $url, PyStringNode $body): void - { - $this->getSessionRequestHelper()->sendRequestWithPyStringNode($method, $this->locatePath($url), $body); - } - - /** - * @When I add :name header equal to :value - */ - public function iAddHeaderEqualTo($name, $value): void - { - $this->getSessionRequestHelper()->addHeaderEqualTo($name, $value); - } - - /** - * @Then print request headers - */ - public function printRequestHeaders(): void - { - $this->getSessionRequestHelper()->printRequestHeaders(); - } - - private function getSessionRequestHelper(): MinkSessionRequestHelper - { - return $this->sessionRequestHelper = $this->sessionRequestHelper - ?: new MinkSessionRequestHelper(new MinkHelper($this->minkSession)); - } -} diff --git a/tests/src/Shared/Infrastructure/Behat/ApiContext/ApiResponseContext.php b/tests/src/Shared/Infrastructure/Behat/ApiContext/ApiResponseContext.php deleted file mode 100644 index fb44c4f2e..000000000 --- a/tests/src/Shared/Infrastructure/Behat/ApiContext/ApiResponseContext.php +++ /dev/null @@ -1,191 +0,0 @@ -minkSession = $minkSession; - } - - public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = ''): void - { - Assert::assertJson($expectedJson, 'The expected value is not a valid json'); - Assert::assertJson($actualJson, 'The actual value is not a valid json'); - - $expected = json_decode($expectedJson); - $actual = json_decode($actualJson); - - Assert::assertThat( - $actual, - new CodelyTvConstraintIsSimilar($expected, 20), // @todo For functional - $message - ); - } - - /** - * @Then the response content should be: - */ - public function theResponseContentShouldBe(PyStringNode $expected): void - { - if ($this->getSessionHelper()->getResponseHeader('content-type') === 'application/json') { - self::assertJsonStringEqualsJsonString( - $this->adaptExpected($expected->getRaw()), - $this->getSessionResponseHelper()->getResponse(), - sprintf('The string "%s" is not equal to the response of the current page', $expected) - ); - } else { - Assert::assertEquals( - $expected->getRaw(), - $this->getSessionResponseHelper()->getResponse(), - sprintf('The string "%s" is not equal to the response of the current page', $expected) - ); - } - } - - /** - * @Then the response should be empty - */ - public function theResponseShouldBeEmpty(): void - { - Assert::assertEmpty( - $this->getSessionResponseHelper()->getResponse(), - 'The response of the current page is not empty' - ); - } - - /** - * @Then print last api response - */ - public function printApiResponse(): void - { - print_r($this->getSessionResponseHelper()->getResponse()); - } - - /** - * @Then the response parameter :name should exist - */ - public function theResponseParameterShouldExist($name): void - { - if (!$this->getSessionHelper()->hasResponseParameter($name)) { - throw new RuntimeException(sprintf('Parameter "%s" does not exists in response', $name)); - } - } - - /** - * @Then the response parameter :name should match :regex - */ - public function theResponseParameterShouldMatch($name, $regex): void - { - $value = $this->getSessionHelper()->getResponseParameter($name); - $errorMessage = vsprintf( - 'The response parameter "%s" is "%s" and it should match "%s" but it does not.', - [$name, $value, $regex] - ); - - Assert::assertRegExp($regex, (string) $value, $errorMessage); - } - - /** - * @Then the response parameter :name should be :expectedValue - */ - public function theResponseParameterShouldBe($name, $expectedValue): void - { - $value = $this->getSessionHelper()->getResponseParameter($name); - Assert::assertEquals($expectedValue, $value); - } - - /** - * @Then print response headers - */ - public function printResponseHeaders(): void - { - print_r($this->getSessionHelper()->getResponseHeaders()); - } - - /** - * @Then the response header :name should be :value - */ - public function theResponseHeaderShouldBe($name, $value): void - { - $this->theHeaderShouldExists($name); - - $header = $this->getSessionHelper()->getResponseHeader($name); - - Assert::assertSame( - $value, - $header, - sprintf('The header "%s" is equal to "%s"', $name, $header) - ); - } - - /** - * @Then the response header :name should exists - */ - public function theHeaderShouldExists($name): void - { - if (!$this->getSessionHelper()->hasResponseHeader($name)) { - throw new RuntimeException(sprintf('The header "%s" does not exists', $name)); - } - } - - /** - * @Then the response status code should be :expectedResponseCode - */ - public function theResponseStatusCodeShouldBe($expectedResponseCode): void - { - Assert::assertSame((int) $expectedResponseCode, $this->minkSession->getStatusCode()); - } - - private function getSessionResponseHelper(): MinkSessionResponseHelper - { - return $this->sessionResponseHelper = $this->sessionResponseHelper ?: new MinkSessionResponseHelper( - $this->getSessionHelper() - ); - } - - private function getSessionHelper(): MinkHelper - { - return $this->sessionHelper = $this->sessionHelper ?: new MinkHelper($this->minkSession); - } - - private function adaptExpected($expectedResponse) - { - return $this->convertRelativeDates($expectedResponse); - } - - private function convertRelativeDates($expectedResponse) - { - if (preg_match_all('/\#date (?[^\#]+)\#/', $expectedResponse, $matches)) { - foreach ($matches['dates'] as $date) { - $expectedResponse = str_replace( - sprintf('#date %s#', $date), - date_to_string(new DateTimeImmutable($date)), - $expectedResponse - ); - } - } - - return $expectedResponse; - } -} diff --git a/tests/src/Shared/Infrastructure/Behat/ApiFeatureContext.php b/tests/src/Shared/Infrastructure/Behat/ApiFeatureContext.php deleted file mode 100644 index d034f24e6..000000000 --- a/tests/src/Shared/Infrastructure/Behat/ApiFeatureContext.php +++ /dev/null @@ -1,48 +0,0 @@ -connections = $connections; - $this->publisher = $publisher; - $this->bus = $bus; - } - - /** @BeforeScenario */ - public function cleanEnvironment(): void - { - $this->connections->clear(); - $this->connections->truncate(); - } - - /** @AfterStep */ - public function publishEvents(): void - { - $publisher = function (DomainEvent $event) { - $this->bus->notify($event); - }; - - while ($this->publisher->hasEventsToPublish()) { - each($publisher, $this->publisher->popPublishedEvents()); - } - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Command/CommandBusSyncWithMiddlewaresTest.php b/tests/src/Shared/Infrastructure/Bus/Command/CommandBusSyncWithMiddlewaresTest.php deleted file mode 100644 index a1e2e6f24..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Command/CommandBusSyncWithMiddlewaresTest.php +++ /dev/null @@ -1,82 +0,0 @@ -markTestSkipped('Temporally middlewares are disabled'); - - $this->commandBus = new SymfonySyncCommandBus(new MessageLoggerMiddleware($this->logger())); - } - - /** @test */ - public function it_should_be_able_to_handle_a_command_with_middlewares(): void - { - $this->commandBus->register(get_class($this->command()), $this->commandHandler()); - - $this->shouldCheckCommandMessageType(); - $this->shouldLog(); - $this->commandHandlerShouldBeCalled(); - - $this->commandBus->dispatch($this->command()); - } - - /** @return LoggerInterface|MockInterface */ - protected function logger() - { - return $this->logger = $this->logger ?: $this->mock(LoggerInterface::class); - } - - protected function shouldLog(): void - { - $this->logger()->shouldReceive('debug')->once()->andReturnNull(); - } - - private function commandHandler(): callable - { - return function ($command) { - $command->name(); - }; - } - - /** @return Command|MockInterface */ - private function command() - { - return $this->command = $this->command ?: $this->mock(Command::class); - } - - private function commandHandlerShouldBeCalled(): void - { - $this->command() - ->shouldReceive('name') - ->once() - ->withNoArgs(); - } - - private function shouldCheckCommandMessageType(): void - { - $this->command() - ->shouldReceive('messageType') - ->once() - ->withNoArgs() - ->andReturn('command'); - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Command/FakeCommand.php b/tests/src/Shared/Infrastructure/Bus/Command/FakeCommand.php deleted file mode 100644 index 4cba23dfe..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Command/FakeCommand.php +++ /dev/null @@ -1,11 +0,0 @@ -commandBus = new SymfonySyncCommandBus([$this->commandHandler()]); - } - - /** - * @test - * @expectedException RuntimeException - */ - public function it_should_be_able_to_handle_a_command(): void - { - $this->commandBus->dispatch(new FakeCommand(Uuid::random())); - } - - /** @test */ - public function it_should_raise_an_exception_dispatching_a_non_registered_command(): void - { - $this->expectException(CommandNotRegisteredError::class); - - $this->commandBus->dispatch($this->mock(Command::class)); - } - - private function commandHandler() - { - return new class - { - public function __invoke(FakeCommand $command) - { - throw new RuntimeException('This works fine!'); - } - }; - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Event/ConstructionTestDomainEvent.php b/tests/src/Shared/Infrastructure/Bus/Event/ConstructionTestDomainEvent.php deleted file mode 100644 index 504283037..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Event/ConstructionTestDomainEvent.php +++ /dev/null @@ -1,25 +0,0 @@ - ['string'], - ]; - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Event/DomainEventTest.php b/tests/src/Shared/Infrastructure/Bus/Event/DomainEventTest.php deleted file mode 100644 index 7b7cf8262..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Event/DomainEventTest.php +++ /dev/null @@ -1,60 +0,0 @@ - $someIdentifier]); - - $this->assertEquals($aggregateId, $event->aggregateId()); - $this->assertEquals($someIdentifier, $event->someIdentifier()); - } - - /** @test */ - public function it_should_not_throw_an_exception_constructing_an_event_with_more_parameters_than_defined(): void - { - $aggregateId = UuidMother::random(); - $someIdentifier = UuidMother::random(); - $nonDeclaredParameter = WordMother::random(); - - $event = new ConstructionTestDomainEvent( - $aggregateId, - [ - 'someIdentifier' => $someIdentifier, - 'nonDeclaredParameter' => $nonDeclaredParameter, - ] - ); - - $this->assertEquals($aggregateId, $event->aggregateId()); - $this->assertEquals($someIdentifier, $event->someIdentifier()); - $this->assertEquals($nonDeclaredParameter, $event->nonDeclaredParameter()); - } - - /** @test */ - public function it_should_throw_an_exception_constructing_an_event_without_a_required_parameter(): void - { - $this->expectException(DomainException::class); - - new ConstructionTestDomainEvent(UuidMother::random(), []); - } - - /** @test */ - public function it_should_throw_an_exception_constructing_an_event_having_a_parameter_with_an_incorrect_type(): void - { - $this->expectException(DomainException::class); - - new ConstructionTestDomainEvent(UuidMother::random(), ['someIdentifier' => ['this is an array']]); - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Event/FakeDomainEvent.php b/tests/src/Shared/Infrastructure/Bus/Event/FakeDomainEvent.php deleted file mode 100644 index e6d4ec6ca..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Event/FakeDomainEvent.php +++ /dev/null @@ -1,20 +0,0 @@ -bus = new SymfonySyncEventBus( - [ - $this->subscriber(), - $this->subscriber(), - ] - ); - } - - /** @test */ - public function it_should_publish_and_handle_one_event(): void - { - $this->bus->notify(new FakeDomainEvent('aggregate id')); - - $this->assertEquals(2, self::$totalTimesCalled); - } - - private function subscriber() - { - return new class() implements DomainEventSubscriber - { - public function __invoke(DomainEvent $unused) - { - SymfonySyncEventBusTest::$totalTimesCalled++; - } - - public static function subscribedTo(): array - { - return [FakeDomainEvent::class]; - } - }; - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Query/FakeQuery.php b/tests/src/Shared/Infrastructure/Bus/Query/FakeQuery.php deleted file mode 100644 index 4cb352774..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Query/FakeQuery.php +++ /dev/null @@ -1,11 +0,0 @@ -number = $number; - } - - public function number(): int - { - return $this->number; - } -} diff --git a/tests/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBusTest.php b/tests/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBusTest.php deleted file mode 100644 index 389e3ad43..000000000 --- a/tests/src/Shared/Infrastructure/Bus/Query/SymfonySyncQueryBusTest.php +++ /dev/null @@ -1,52 +0,0 @@ -queryBus = new SymfonySyncQueryBus([$this->queryHandler()]); - } - - /** - * @test - * @expectedException RuntimeException - */ - public function it_should_return_a_response_successfully(): void - { - $this->queryBus->ask(new FakeQuery()); - } - - /** @test */ - public function it_should_raise_an_exception_dispatching_a_non_registered_query(): void - { - $this->expectException(QueryNotRegisteredError::class); - - $this->queryBus->ask($this->mock(Query::class)); - } - - private function queryHandler() - { - return new class - { - public function __invoke(FakeQuery $query) - { - throw new RuntimeException('This works fine!'); - } - }; - } -} diff --git a/tests/src/Shared/Infrastructure/Mink/MinkHelper.php b/tests/src/Shared/Infrastructure/Mink/MinkHelper.php deleted file mode 100644 index d03929f8d..000000000 --- a/tests/src/Shared/Infrastructure/Mink/MinkHelper.php +++ /dev/null @@ -1,165 +0,0 @@ -session = $session; - } - - public function setRequestHeader($header, $value): void - { - $this->getClient()->setServerParameter($header, $value); - } - - public function addRequestHttpBasicAuthentication($username, $password): void - { - $this->getClient()->setServerParameter('PHP_AUTH_USER', $username); - $this->getClient()->setServerParameter('PHP_AUTH_PW', $password); - } - - public function sendRequest($method, $url, array $optionalParams = []): Crawler - { - $defaultOptionalParams = [ - 'parameters' => [], - 'files' => [], - 'server' => ['HTTP_ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json'], - 'content' => null, - 'changeHistory' => true, - ]; - - $optionalParams = array_merge($defaultOptionalParams, $optionalParams); - - $crawler = $this->getClient()->request( - $method, - $url, - $optionalParams['parameters'], - $optionalParams['files'], - $optionalParams['server'], - $optionalParams['content'], - $optionalParams['changeHistory'] - ); - - $this->resetRequestStuff(); - - return $crawler; - } - - public function getRequestHeaders(): array - { - return $this->normalizeHeaders($this->getRequest()->headers->all()); - } - - public function getResponse(): string - { - return $this->getSession()->getPage()->getContent(); - } - - public function getResponseHeaders(): array - { - return $this->normalizeHeaders( - array_change_key_case($this->getSession()->getResponseHeaders(), CASE_LOWER) - ); - } - - public function responseShouldContain($needle): void - { - if (strpos($this->clearString($this->getResponse()), $this->clearString($needle)) === false) { - throw new RuntimeException(sprintf('The response do not contain %s', $needle)); - } - } - - public function responseShouldNotContain($needle): void - { - if (strpos($this->clearString($this->getResponse()), $this->clearString($needle)) === true) { - throw new RuntimeException(sprintf('The response do not contain %s', $needle)); - } - } - - public function hasResponseHeader($name): bool - { - return array_key_exists($name, $this->getResponseHeaders()); - } - - public function getResponseHeader($name) - { - return get_in([$name], $this->getResponseHeaders()); - } - - public function hasResponseParameter($name): bool - { - return array_key_exists($name, $this->getResponseParameters()); - } - - public function getResponseParameter($name) - { - return get_in([$name], $this->getResponseParameters()); - } - - public function resetServerParameters(): void - { - $this->getClient()->setServerParameters([]); - } - - public function getNodeElementByXpath($query) - { - return $this->getSession()->getPage()->find('xpath', $query); - } - - public function getRequest(): Request - { - return $this->getClient()->getRequest(); - } - - private function getSession(): Session - { - return $this->session; - } - - private function getDriver(): DriverInterface - { - return $this->getSession()->getDriver(); - } - - private function getClient(): Client - { - return $this->getDriver()->getClient(); - } - - /** FIXME: The content can be different than json, check the content-type header */ - private function getResponseParameters() - { - return json_decode($this->getSession()->getPage()->getContent(), true); - } - - private function normalizeHeaders(array $headers): array - { - return array_map('implode', array_filter($headers)); - } - - private function resetRequestStuff(): void - { - $this->getSession()->reset(); - $this->resetServerParameters(); - } - - private function clearString($string) - { - return preg_replace('/\s+/S', ' ', $string); - } -} diff --git a/tests/src/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php b/tests/src/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php deleted file mode 100644 index 870da6717..000000000 --- a/tests/src/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php +++ /dev/null @@ -1,58 +0,0 @@ -sessionHelper = $sessionHelper; - } - - public function sendRequest($method, $url, array $optionalParams = []): void - { - $this->request($method, $url, $optionalParams); - } - - /** - * @todo : Fix parameters from hash, now is a simple solution - */ - public function sendRequestWithTableNode($method, $url, TableNode $parameters): void - { - $this->request($method, $url, ['parameters' => $parameters->getRowsHash()]); - } - - public function sendRequestWithPyStringNode($method, $url, PyStringNode $body): void - { - $this->request($method, $url, ['content' => $body->getRaw()]); - } - - public function addHeaderEqualTo($name, $value): void - { - $this->sessionHelper->setRequestHeader($name, $value); - } - - public function printRequestHeaders(): void - { - print_r($this->sessionHelper->getRequestHeaders()); - } - - public function request($method, $url, array $optionalParams = []): Crawler - { - return $this->sessionHelper->sendRequest($method, $url, $optionalParams); - } - - public function addHttpBasicAuthentication($username, $password): void - { - $this->sessionHelper->addRequestHttpBasicAuthentication($username, $password); - } -} diff --git a/tests/src/Shared/Infrastructure/Mink/MinkSessionResponseHelper.php b/tests/src/Shared/Infrastructure/Mink/MinkSessionResponseHelper.php deleted file mode 100644 index dfca129b6..000000000 --- a/tests/src/Shared/Infrastructure/Mink/MinkSessionResponseHelper.php +++ /dev/null @@ -1,21 +0,0 @@ -sessionHelper = $sessionHelper; - } - - public function getResponse(): string - { - return $this->sessionHelper->getResponse(); - } -} diff --git a/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsEqual.php b/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsEqual.php deleted file mode 100644 index e99bbe7df..000000000 --- a/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsEqual.php +++ /dev/null @@ -1,35 +0,0 @@ -constraint = new CodelyTvConstraintIsEqual($value, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - - public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) - { - return new static($value, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - - public function match(&$actual) - { - return $this->constraint->evaluate($actual, '', true); - } - - public function __toString() - { - return 'Is equal'; - } -} diff --git a/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php b/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php deleted file mode 100644 index 18dee5747..000000000 --- a/tests/src/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php +++ /dev/null @@ -1,41 +0,0 @@ -constraint = new CodelyTvConstraintIsSimilar( - $value, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ); - } - - public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) - { - return new static($value, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - - public function match(&$actual) - { - return $this->constraint->evaluate($actual, '', true); - } - - public function __toString() - { - return 'Is similar'; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootArraySimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootArraySimilarComparator.php deleted file mode 100644 index 17ebec188..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootArraySimilarComparator.php +++ /dev/null @@ -1,52 +0,0 @@ -contains($expected, $actual) || count($expected) !== count($actual)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting the collection of AGs contains all the expected elements.' - ); - } - } - - private function contains(array $expectedArray, array $actualArray): bool - { - $exists = function (AggregateRoot $expected) use ($actualArray) { - return any( - function (AggregateRoot $actual) use ($expected) { - return isSimilar($expected, $actual); - }, - $actualArray - ); - }; - - return all($exists, $expectedArray); - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootSimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootSimilarComparator.php deleted file mode 100644 index ea9b8af85..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/AggregateRootSimilarComparator.php +++ /dev/null @@ -1,90 +0,0 @@ -pullDomainEvents(); - - if (!$this->aggregateRootsAreSimilar($expected, $actualEntity)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting the aggregate roots are equal.' - ); - } - } - - /** - * @param AggregateRoot $expected - * @param AggregateRoot $actual - */ - private function aggregateRootsAreSimilar($expected, $actual): bool - { - if (!$this->aggregateRootsAreTheSameClass($expected, $actual)) { - return false; - } - - return $this->aggregateRootPropertiesAreSimilar($expected, $actual); - } - - /** - * @param AggregateRoot $expected - * @param AggregateRoot $actual - */ - private function aggregateRootsAreTheSameClass($expected, $actual): bool - { - return get_class($expected) === get_class($actual); - } - - /** - * @param AggregateRoot $expected - * @param AggregateRoot $actual - */ - private function aggregateRootPropertiesAreSimilar($expected, $actual): bool - { - $expectedReflected = new ReflectionObject($expected); - $actualReflected = new ReflectionObject($actual); - - foreach ($expectedReflected->getProperties() as $expectedReflectedProperty) { - $actualReflectedProperty = $actualReflected->getProperty($expectedReflectedProperty->getName()); - - $expectedReflectedProperty->setAccessible(true); - $actualReflectedProperty->setAccessible(true); - - $expectedProperty = $expectedReflectedProperty->getValue($expected); - $actualProperty = $actualReflectedProperty->getValue($actual); - - if (!isSimilar($expectedProperty, $actualProperty)) { - return false; - } - } - - return true; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeSimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeSimilarComparator.php deleted file mode 100644 index 305867def..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeSimilarComparator.php +++ /dev/null @@ -1,50 +0,0 @@ -sub($intervalWithDelta) || $actual > $expectedUpper->add($intervalWithDelta)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->dateTimeToString($expected), - $this->dateTimeToString($actual), - false, - 'Failed asserting that two DateTime objects are equal.' - ); - } - } - - protected function dateTimeToString(DateTimeInterface $datetime): string - { - return $datetime->format(DateTime::ATOM) ?: 'Invalid DateTime object'; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeStringSimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeStringSimilarComparator.php deleted file mode 100644 index e25f35a9c..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DateTimeStringSimilarComparator.php +++ /dev/null @@ -1,72 +0,0 @@ -isValidDateTimeString($expected) && - $this->isValidDateTimeString($actual); - } - - private function isValidDateTimeString($expected): bool - { - $isValid = true; - - try { - new DateTimeImmutable($expected); - } catch (Throwable $throwable) { - $isValid = false; - } - - return $isValid; - } - - public function assertEquals( - $expected, - $actual, - $delta = 0.0, - $canonicalize = false, - $ignoreCase = false, - array &$processed = [] - ): void { - $expectedDate = new DateTimeImmutable($expected); - $actualDate = new DateTimeImmutable($actual); - - $normalizedDelta = $delta === 0.0 ? 10 : $delta; - $intervalWithDelta = new DateInterval(sprintf('PT%sS', abs($normalizedDelta))); - - if ($actualDate < $expectedDate->sub($intervalWithDelta) || - $actualDate > $expectedDate->add($intervalWithDelta)) { - throw new ComparisonFailure( - $expectedDate, - $actualDate, - $this->dateTimeToString($expectedDate), - $this->dateTimeToString($actualDate), - false, - 'Failed asserting that two DateTime strings are equal.' - ); - } - } - - protected function dateTimeToString(DateTimeInterface $datetime): string - { - $string = $datetime->format(DateTime::ATOM); - - return $string ?: 'Invalid DateTime object'; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventArraySimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventArraySimilarComparator.php deleted file mode 100644 index 66eed2c1a..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventArraySimilarComparator.php +++ /dev/null @@ -1,52 +0,0 @@ -contains($expected, $actual) || count($expected) !== count($actual)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting the collection of Events contains all the expected elements.' - ); - } - } - - private function contains(array $expectedArray, array $actualArray): bool - { - $exists = function (DomainEvent $expected) use ($actualArray) { - return any( - function (DomainEvent $actual) use ($expected) { - return isSimilar($expected, $actual); - }, - $actualArray - ); - }; - - return all($exists, $expectedArray); - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventSimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventSimilarComparator.php deleted file mode 100644 index 16773751c..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/DomainEventSimilarComparator.php +++ /dev/null @@ -1,91 +0,0 @@ -areSimilar($expected, $actual)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting the events are equal.' - ); - } - } - - /** - * @param DomainEvent $expected - * @param DomainEvent $actual - */ - private function areSimilar($expected, $actual): bool - { - if (!$this->areTheSameClass($expected, $actual)) { - return false; - } - - return $this->propertiesAreSimilar($expected, $actual); - } - - /** - * @param DomainEvent $expected - * @param DomainEvent $actual - */ - private function areTheSameClass($expected, $actual): bool - { - return get_class($expected) === get_class($actual); - } - - /** - * @param DomainEvent $expected - * @param DomainEvent $actual - */ - private function propertiesAreSimilar($expected, $actual): bool - { - $expectedReflected = new ReflectionObject($expected); - $actualReflected = new ReflectionObject($actual); - - foreach ($expectedReflected->getProperties() as $expectedReflectedProperty) { - if (!in_array($expectedReflectedProperty->getName(), self::$ignoredAttributes, false)) { - $actualReflectedProperty = $actualReflected->getProperty($expectedReflectedProperty->getName()); - - $expectedReflectedProperty->setAccessible(true); - $actualReflectedProperty->setAccessible(true); - - $expectedProperty = $expectedReflectedProperty->getValue($expected); - $actualProperty = $actualReflectedProperty->getValue($actual); - - if (!isSimilar($expectedProperty, $actualProperty)) { - return false; - } - } - } - - return true; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/StringableObjectSimilarComparator.php b/tests/src/Shared/Infrastructure/PHPUnit/Comparator/StringableObjectSimilarComparator.php deleted file mode 100644 index e9f96677f..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Comparator/StringableObjectSimilarComparator.php +++ /dev/null @@ -1,36 +0,0 @@ -isStringable($expected) && !is_object($actual)) || - ($this->isStringable($actual) && !is_object($expected)); - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void - { - if (gettype($expected) !== gettype($actual)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting that stringable objects are similar.' - ); - } - } - - private function isStringable($possibleStringable): bool - { - return is_object($possibleStringable) && method_exists($possibleStringable, '__toString'); - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsEqual.php b/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsEqual.php deleted file mode 100644 index a25000717..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsEqual.php +++ /dev/null @@ -1,39 +0,0 @@ -register(new StringableObjectSimilarComparator()); - - try { - $comparator = $comparatorFactory->getComparatorFor($other, $this->value); - - $comparator->assertEquals($this->value, $other, $this->delta); - } catch (ComparisonFailure $f) { - if (!$returnResult) { - throw new ExpectationFailedException( - trim($description . "\n" . $f->getMessage()), - $f - ); - } - - $isValid = false; - } - - return $isValid; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsSimilar.php b/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsSimilar.php deleted file mode 100644 index c59bf14b9..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Constraint/CodelyTvConstraintIsSimilar.php +++ /dev/null @@ -1,51 +0,0 @@ -register(new AggregateRootArraySimilarComparator()); - $comparatorFactory->register(new AggregateRootSimilarComparator()); - $comparatorFactory->register(new DomainEventArraySimilarComparator()); - $comparatorFactory->register(new DomainEventSimilarComparator()); - $comparatorFactory->register(new DateTimeSimilarComparator()); - $comparatorFactory->register(new DateTimeStringSimilarComparator()); - $comparatorFactory->register(new StringableObjectSimilarComparator()); - - try { - $comparator = $comparatorFactory->getComparatorFor($other, $this->value); - - $comparator->assertEquals($this->value, $other, $this->delta); - } catch (ComparisonFailure $f) { - if (!$returnResult) { - throw new ExpectationFailedException( - trim($description . "\n" . $f->getMessage()), - $f - ); - } - - $isValid = false; - } - - return $isValid; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/FunctionalTestCase.php b/tests/src/Shared/Infrastructure/PHPUnit/FunctionalTestCase.php deleted file mode 100644 index a29ff7c32..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/FunctionalTestCase.php +++ /dev/null @@ -1,27 +0,0 @@ - 'test']); - - parent::setUp(); - } - - protected function service($id) - { - return self::$container->get($id); - } - - protected function parameter($parameter) - { - return self::$container->getParameter($parameter); - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleFunctionalTestCase.php b/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleFunctionalTestCase.php deleted file mode 100644 index ef3fe1690..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleFunctionalTestCase.php +++ /dev/null @@ -1,62 +0,0 @@ -arrange(); - }, - $this->environmentArrangers() - ); - } - - protected function tearDown() - { - each( - function (EnvironmentArranger $arranger) { - $arranger->close(); - }, - $this->environmentArrangers() - ); - - parent::tearDown(); - } - - protected function clearUnitOfWork(): void - { - $this->service(EntityManager::class)->clear(); - } - - protected function notify(DomainEvent $event): void - { - $this->assertNull($this->domainEventPublisher()->publish($event)); - } - - protected function assertSimilar($expected, $actual): void - { - assertSimilar($expected, $actual); - } - - private function domainEventPublisher(): DomainEventPublisher - { - return $this->service('codely.infrastructure.domain_event_publisher'); - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleUnitTestCase.php b/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleUnitTestCase.php deleted file mode 100644 index 276d7e043..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/Module/ModuleUnitTestCase.php +++ /dev/null @@ -1,148 +0,0 @@ -queryBus = $this->queryBus ?: $this->mock(QueryBus::class); - } - - /** @return DomainEventPublisher|MockInterface */ - protected function domainEventPublisher() - { - return $this->domainEventPublisher = $this->domainEventPublisher ?: $this->mock(DomainEventPublisher::class); - } - - /** @return CommandBus|MockInterface */ - protected function commandBus() - { - return $this->commandBus = $this->commandBus ?: $this->mock(CommandBus::class); - } - - /** @return LoggerInterface|MockInterface */ - protected function logger() - { - return $this->logger = $this->logger ?: $this->mock(LoggerInterface::class); - } - - protected function assertAskResponse(Query $query, Response $response, callable $handler): void - { - $this->assertEquals($response, $this->ask($query, $handler), 'QueryBus did not returned the expected response'); - } - - protected function assertAskNullResponse(Query $query, callable $handler): void - { - $this->assertNull($this->ask($query, $handler), 'QueryBus did not returned the expected response'); - } - - protected function assertAskThrowsException($exceptionClass, Query $query, callable $handler): void - { - $this->expectException($exceptionClass); - - $this->ask($query, $handler); - } - - protected function shouldAsk(Query $query, Response $response = null): void - { - $this->queryBus() - ->shouldReceive('ask') - ->once() - ->with(similarTo($query)) - ->andReturn($response); - } - - protected function shouldAskThrowingException(Query $query, $exception): void - { - $this->queryBus() - ->shouldReceive('ask') - ->once() - ->with(equalTo($query)) - ->andThrow($exception); - } - - protected function notify(DomainEvent $event, callable $subscriber): void - { - $subscriber($event); - } - - protected function shouldNotifyThrowingException( - DomainEvent $event, - callable $subscriber, - string $exceptionClass - ): void { - $this->expectException($exceptionClass); - - $this->notify($event, $subscriber); - } - - protected function dispatch(Command $command, callable $handler): void - { - $handler($command); - } - - /** @param DomainEvent[] $events */ - protected function shouldPublishDomainEvents(DomainEvent ...$events): void - { - $this->domainEventPublisher() - ->shouldReceive('publish') - ->once() - ->with(...map($this->addSimilarTo(), $events)) - ->andReturnNull(); - } - - protected function shouldNotPublishDomainEvents(): void - { - $this->domainEventPublisher() - ->shouldNotReceive('publish'); - } - - protected function shouldLog($level): void - { - $this->logger()->shouldReceive($level)->once()->andReturnNull(); - } - - protected function shouldLogMessage($level, $message, array $context = []): void - { - $this->logger() - ->shouldReceive($level) - ->once() - ->with($message, $context) - ->andReturnNull(); - } - - private function ask(Query $query, callable $handler) - { - return $handler($query); - } - - private function addSimilarTo(): callable - { - return function (DomainEvent $event) { - return similarTo($event); - }; - } -} diff --git a/tests/src/Shared/Infrastructure/PHPUnit/UnitTestCase.php b/tests/src/Shared/Infrastructure/PHPUnit/UnitTestCase.php deleted file mode 100644 index 865658581..000000000 --- a/tests/src/Shared/Infrastructure/PHPUnit/UnitTestCase.php +++ /dev/null @@ -1,22 +0,0 @@ -assertSame($expected, date_to_string($date)); - } - - /** - * @test - * @dataProvider validDatesConversions - */ - public function it_should_convert_a_date_from_milliseconds(DateTimeImmutable $expected, string $stringDate): void - { - $this->assertEquals($expected, string_to_date($stringDate)); - } - - public function validDatesConversions(): array - { - return [ - [ - 'date' => DateTimeMother::create('1993-06-26 10:00:00 GMT+0200'), - 'string' => '741081600000', - ], - [ - 'date' => DateTimeMother::create('1994-09-29 15:00:00 GMT+0200'), - 'string' => '780843600000', - ], - - [ - 'date' => DateTimeMother::create('2020-01-15 22:23:24 GMT+0500'), - 'string' => '1579109004000', - ], - [ - 'date' => DateTimeMother::create('2016-10-03 12:41:32.980000', DateTimeZoneMother::UTC()), - 'string' => '1475498492980', - ], - ]; - } -} diff --git a/tests/src/Shared/utils.php b/tests/src/Shared/utils.php deleted file mode 100644 index 31e6affec..000000000 --- a/tests/src/Shared/utils.php +++ /dev/null @@ -1,63 +0,0 @@ -evaluate($value, '', true); -} - -function assertSimilar( - $expected, - $actual, - $message = '', - $delta = 0.0, - $maxDepth = 10, - $canonicalize = false, - $ignoreCase = false -) { - $constraint = new CodelyTvConstraintIsSimilar($expected, $delta, $maxDepth, $canonicalize, $ignoreCase); - - Assert::assertThat($actual, $constraint, $message); -} - -function similarTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) -{ - return CodelyTvMatcherIsSimilar::equalTo($value, $delta, $maxDepth, $canonicalize, $ignoreCase); -} - -function isEqual($expected, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) -{ - $constraint = new CodelyTvConstraintIsEqual($expected, $delta, $maxDepth, $canonicalize, $ignoreCase); - - return $constraint->evaluate($value, '', true); -} - -function assertEquals( - $expected, - $actual, - $message = '', - $delta = 0.0, - $maxDepth = 10, - $canonicalize = false, - $ignoreCase = false -) { - $constraint = new CodelyTvConstraintIsEqual($expected, $delta, $maxDepth, $canonicalize, $ignoreCase); - - Assert::assertThat($actual, $constraint, $message); -} - -function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) -{ - return CodelyTvMatcherIsEqual::equalTo($value, $delta, $maxDepth, $canonicalize, $ignoreCase); -}