diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c5dc2df4..39fe081e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main pull_request: jobs: @@ -12,9 +12,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - run: docker-compose pull + - run: docker compose pull - name: 🎻 Install dependencies run: make composer-install @@ -28,6 +28,12 @@ jobs: - 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 @@ -35,14 +41,14 @@ jobs: sleep 2 done - - name: πŸ§ͺ Wait for the elasticsearch to get up + - 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 the message broker to get up + - name: 🐰 Wait for RabbitMQ to get up run: | while ! make ping-rabbitmq &>/dev/null; do echo "Waiting for RabbitMQ connection..." diff --git a/.gitignore b/.gitignore index ee27af29d..a4b4f3437 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.env.*.local /apps/*/*/var/ +!/apps/*/*/var/.gitkeep /apps/*/*/build/ !/apps/*/*/build/supervisor/.gitkeep diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php deleted file mode 100644 index bb744c831..000000000 --- a/.php-cs-fixer.dist.php +++ /dev/null @@ -1,23 +0,0 @@ -in( - [ - __DIR__ . '/apps/backoffice/backend/src', - __DIR__ . '/apps/backoffice/frontend/src', - __DIR__ . '/apps/mooc/backend/src', - __DIR__ . '/apps/mooc/frontend/src', - __DIR__ . '/src', - __DIR__ . '/tests', - ] -); - -$config = new PhpCsFixer\Config(); - -return $config->setRules( - [ - '@PSR12' => true, - 'strict_param' => true, - 'modernize_strpos' => true, - 'array_syntax' => ['syntax' => 'short'], - ] -)->setFinder($finder); diff --git a/Dockerfile b/Dockerfile index 5e677d79f..dd0f725bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,31 +1,19 @@ -FROM php:8.1.6-fpm-alpine +FROM php:8.3-fpm-alpine WORKDIR /app -RUN wget https://github.com/FriendsOfPHP/pickle/releases/download/v0.7.9/pickle.phar \ - && mv pickle.phar /usr/local/bin/pickle \ - && chmod +x /usr/local/bin/pickle - RUN apk --update upgrade \ - && apk add --no-cache autoconf automake make gcc g++ bash icu-dev libzip-dev rabbitmq-c rabbitmq-c-dev \ - && 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 \ zip \ pdo_mysql -RUN pickle install apcu@5.1.21 - -ADD etc/infrastructure/php/extensions/rabbitmq.sh /root/install-rabbitmq.sh -ADD etc/infrastructure/php/extensions/xdebug.sh /root/install-xdebug.sh -RUN apk add git -RUN sh /root/install-rabbitmq.sh -RUN sh /root/install-xdebug.sh - -RUN docker-php-ext-enable \ - amqp \ - apcu \ - opcache +RUN docker-php-ext-enable amqp apcu opcache RUN curl -sS https://get.symfony.com/cli/installer | bash -s - --install-dir /usr/local/bin diff --git a/Makefile b/Makefile index ca2150c78..efec06c20 100644 --- a/Makefile +++ b/Makefile @@ -1,100 +1,54 @@ current-dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -.PHONY: build -build: deps start - -.PHONY: deps -deps: composer-install - -# 🐘 Composer -composer-env-file: - @if [ ! -f .env.local ]; then echo '' > .env.local; fi - -.PHONY: composer-install -composer-install: CMD=install - -.PHONY: composer-update -composer-update: CMD=update - -.PHONY: composer-require -composer-require: CMD=require -composer-require: INTERACTIVE=-ti --interactive - -.PHONY: composer-require-module -composer-require-module: CMD=require $(module) -composer-require-module: INTERACTIVE=-ti --interactive - -.PHONY: composer -composer composer-install composer-update composer-require composer-require-module: composer-env-file +composer-install: @docker run --rm $(INTERACTIVE) --volume $(current-dir):/app --user $(id -u):$(id -g) \ - composer:2.3.7 $(CMD) \ + composer:2.6.4 install \ --ignore-platform-reqs \ --no-ansi -.PHONY: reload -reload: composer-env-file - @docker-compose exec php-fpm kill -USR2 1 - @docker-compose exec nginx nginx -s reload - -.PHONY: test -test: composer-env-file +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 -.PHONY: static-analysis -static-analysis: composer-env-file - docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/psalm +static-analysis: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/psalm --output-format=github --shepherd -.PHONY: lint lint: - docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/php-cs-fixer fix --config .php-cs-fixer.dist.php --allow-risky=yes --dry-run + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/ecs check -.PHONY: run-tests -run-tests: composer-env-file - mkdir -p build/test_results/phpunit - ./vendor/bin/phpunit --exclude-group='disabled' --log-junit build/test_results/phpunit/junit.xml --testsuite backoffice - ./vendor/bin/phpunit --exclude-group='disabled' --log-junit build/test_results/phpunit/junit.xml --testsuite mooc - ./vendor/bin/phpunit --exclude-group='disabled' --log-junit build/test_results/phpunit/junit.xml --testsuite shared - ./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 -.PHONY: start -start: CMD=up --build -d +mess-detector: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpmd apps,src,tests github phpmd.xml -.PHONY: stop -stop: CMD=stop +start: + @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 -.PHONY: destroy -destroy: CMD=down +stop: + UID=${shell id -u} GID=${shell id -g} docker compose stop -# Usage: `make doco CMD="ps --services"` -# Usage: `make doco CMD="build --parallel --pull --force-rm --no-cache"` -.PHONY: doco -doco start stop destroy: composer-env-file - UID=${shell id -u} GID=${shell id -g} docker-compose $(CMD) +destroy: + UID=${shell id -u} GID=${shell id -g} docker compose down -.PHONY: rebuild -rebuild: composer-env-file - docker-compose build --pull --force-rm --no-cache - make deps +rebuild: + docker compose build --pull --force-rm --no-cache + make install make start -.PHONY: ping-mysql ping-mysql: @docker exec codely-php_ddd_skeleton-mooc-mysql mysqladmin --user=root --password= --host "127.0.0.1" ping --silent -.PHONY: ping-elasticsearch ping-elasticsearch: @curl -I -XHEAD localhost:9200 -.PHONY: ping-rabbitmq ping-rabbitmq: @docker exec codely-php_ddd_skeleton-rabbitmq rabbitmqctl ping --silent clean-cache: @rm -rf apps/*/*/var - @docker exec codely-php_ddd_skeleton-backoffice_backend-php ./apps/backoffice/backend/bin/console cache:warmup - @docker exec codely-php_ddd_skeleton-backoffice_frontend-php ./apps/backoffice/frontend/bin/console cache:warmup @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 dd94d40cc..85d72efbe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@

- Codely logo + + + + Codely logo +

@@ -9,9 +13,10 @@

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

@@ -63,8 +68,8 @@ 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): 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 @@ -106,6 +111,7 @@ src ``` #### 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 `Specification` pattern also known as `Criteria` pattern. So we add a `searchByCriteria` method. @@ -114,33 +120,47 @@ 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/Courses/Domain/Course.php). All aggregates should extend the [AggregateRoot](src/Shared/Domain/Aggregate/AggregateRoot.php). ### Command Bus + 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 +1. [Sync](src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php) using the Symfony Message Bus. + ### Query 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/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 shown 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 the [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: -* [πŸ‡ͺπŸ‡Έ 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/) + +- [πŸ‡ͺπŸ‡Έ 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/apps/backoffice/backend/bin/console b/apps/backoffice/backend/bin/console index ac7f81a9a..29bc8b83f 100755 --- a/apps/backoffice/backend/bin/console +++ b/apps/backoffice/backend/bin/console @@ -12,7 +12,7 @@ if (false === in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { set_time_limit(0); -require dirname(__DIR__) . '../../../vendor/autoload.php'; +require dirname(__DIR__) . '/../../../vendor/autoload.php'; if (!class_exists(Application::class)) { throw new RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.'); diff --git a/apps/backoffice/backend/config/bundles.php b/apps/backoffice/backend/config/bundles.php index 7de452252..15c297c9f 100644 --- a/apps/backoffice/backend/config/bundles.php +++ b/apps/backoffice/backend/config/bundles.php @@ -1,7 +1,9 @@ ['all' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], - // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] + Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], + // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] ]; diff --git a/apps/backoffice/backend/public/index.php b/apps/backoffice/backend/public/index.php index 8c109ee2d..45fd7eab5 100644 --- a/apps/backoffice/backend/public/index.php +++ b/apps/backoffice/backend/public/index.php @@ -1,5 +1,7 @@ handle($request); $response->send(); $kernel->terminate($request, $response); diff --git a/apps/backoffice/backend/src/BackofficeBackendKernel.php b/apps/backoffice/backend/src/BackofficeBackendKernel.php index 25c8aa935..321bf5524 100644 --- a/apps/backoffice/backend/src/BackofficeBackendKernel.php +++ b/apps/backoffice/backend/src/BackofficeBackendKernel.php @@ -9,37 +9,38 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; + use function dirname; -final class BackofficeBackendKernel extends Kernel +class BackofficeBackendKernel extends Kernel { - use MicroKernelTrait; - - private const CONFIG_EXTS = '.{xml,yaml}'; - - public function registerBundles(): iterable - { - $contents = require $this->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'); - } + use MicroKernelTrait; + + private const string CONFIG_EXTS = '.{xml,yaml}'; + + public function registerBundles(): iterable + { + $contents = require $this->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 index 7b1570dbb..4fcf89a26 100644 --- a/apps/backoffice/backend/src/Controller/Courses/CoursesGetController.php +++ b/apps/backoffice/backend/src/Controller/Courses/CoursesGetController.php @@ -10,43 +10,42 @@ use CodelyTv\Shared\Domain\Bus\Query\QueryBus; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; + use function Lambdish\Phunctional\map; -final class CoursesGetController +final readonly class CoursesGetController { - public function __construct(private QueryBus $queryBus) - { - } - - public function __invoke(Request $request): JsonResponse - { - $orderBy = $request->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'), - null === $orderBy ? null : (string) $orderBy, - null === $order ? null : (string) $order, - null === $limit ? null : (int) $limit, - null === $offset ? null : (int) $offset - ) - ); - - return new JsonResponse( - map( - fn (BackofficeCourseResponse $course) => [ - 'id' => $course->id(), - 'name' => $course->name(), - 'duration' => $course->duration(), - ], - $response->courses() - ), - 200, - ['Access-Control-Allow-Origin' => '*'] - ); - } + public function __construct(private QueryBus $queryBus) {} + + public function __invoke(Request $request): JsonResponse + { + $orderBy = $request->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 index af32d30ea..fe2256009 100644 --- a/apps/backoffice/backend/src/Controller/HealthCheck/HealthCheckGetController.php +++ b/apps/backoffice/backend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -9,12 +9,12 @@ final class HealthCheckGetController { - public function __invoke(Request $request): JsonResponse - { - return new JsonResponse( - [ - 'backoffice-backend' => 'ok', - ] - ); - } + public function __invoke(Request $request): JsonResponse + { + return new JsonResponse( + [ + 'backoffice-backend' => 'ok', + ] + ); + } } diff --git a/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php b/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php index 6326b58fb..e0ae59059 100644 --- a/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php +++ b/apps/backoffice/backend/src/Controller/Metrics/MetricsController.php @@ -9,17 +9,15 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -final class MetricsController +final readonly class MetricsController { - public function __construct(private PrometheusMonitor $monitor) - { - } + public function __construct(private PrometheusMonitor $monitor) {} - public function __invoke(Request $request): Response - { - $renderer = new RenderTextFormat(); - $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); + public function __invoke(Request $request): Response + { + $renderer = new RenderTextFormat(); + $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); - return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); - } + return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); + } } diff --git a/apps/backoffice/backend/var/.gitkeep b/apps/backoffice/backend/var/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/backoffice/frontend/bin/console b/apps/backoffice/frontend/bin/console index 5e2d7ebf2..d680ab53c 100755 --- a/apps/backoffice/frontend/bin/console +++ b/apps/backoffice/frontend/bin/console @@ -12,7 +12,7 @@ if (false === in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { set_time_limit(0); -require dirname(__DIR__) . '../../../vendor/autoload.php'; +require dirname(__DIR__) . '/../../../vendor/autoload.php'; if (!class_exists(Application::class)) { throw new RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.'); diff --git a/apps/backoffice/frontend/config/bundles.php b/apps/backoffice/frontend/config/bundles.php index c44a868b7..ef8009558 100644 --- a/apps/backoffice/frontend/config/bundles.php +++ b/apps/backoffice/frontend/config/bundles.php @@ -1,8 +1,10 @@ ['all' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] + Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['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/public/index.php b/apps/backoffice/frontend/public/index.php index af90aac44..6dd7c188e 100644 --- a/apps/backoffice/frontend/public/index.php +++ b/apps/backoffice/frontend/public/index.php @@ -1,5 +1,7 @@ handle($request); $response->send(); $kernel->terminate($request, $response); diff --git a/apps/backoffice/frontend/src/BackofficeFrontendKernel.php b/apps/backoffice/frontend/src/BackofficeFrontendKernel.php index 2b5e5247b..ae109b24a 100644 --- a/apps/backoffice/frontend/src/BackofficeFrontendKernel.php +++ b/apps/backoffice/frontend/src/BackofficeFrontendKernel.php @@ -9,37 +9,38 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; + use function dirname; -final class BackofficeFrontendKernel extends Kernel +class BackofficeFrontendKernel extends Kernel { - use MicroKernelTrait; - - private const CONFIG_EXTS = '.{xml,yaml}'; - - public function registerBundles(): iterable - { - $contents = require $this->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'); - } + use MicroKernelTrait; + + private const string CONFIG_EXTS = '.{xml,yaml}'; + + public function registerBundles(): iterable + { + $contents = require $this->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 index 21bc0f4c6..c27dd2396 100644 --- a/apps/backoffice/frontend/src/Command/ImportCoursesToElasticsearchCommand.php +++ b/apps/backoffice/frontend/src/Command/ImportCoursesToElasticsearchCommand.php @@ -12,21 +12,21 @@ final class ImportCoursesToElasticsearchCommand extends Command { - public function __construct( - private MySqlBackofficeCourseRepository $mySqlRepository, - private ElasticsearchBackofficeCourseRepository $elasticRepository - ) { - parent::__construct(); - } + public function __construct( + private readonly MySqlBackofficeCourseRepository $mySqlRepository, + private readonly ElasticsearchBackofficeCourseRepository $elasticRepository + ) { + parent::__construct(); + } - public function execute(InputInterface $input, OutputInterface $output): int - { - $courses = $this->mySqlRepository->searchAll(); + public function execute(InputInterface $input, OutputInterface $output): int + { + $courses = $this->mySqlRepository->searchAll(); - foreach ($courses as $course) { - $this->elasticRepository->save($course); - } + foreach ($courses as $course) { + $this->elasticRepository->save($course); + } - return 0; - } + return 0; + } } diff --git a/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php b/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php index 549fcd62b..a9604d1b2 100644 --- a/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php +++ b/apps/backoffice/frontend/src/Controller/Courses/CoursesGetWebController.php @@ -6,31 +6,31 @@ use CodelyTv\Mooc\CoursesCounter\Application\Find\CoursesCounterResponse; use CodelyTv\Mooc\CoursesCounter\Application\Find\FindCoursesCounterQuery; -use CodelyTv\Shared\Domain\ValueObject\Uuid; +use CodelyTv\Shared\Domain\ValueObject\SimpleUuid; use CodelyTv\Shared\Infrastructure\Symfony\WebController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; final class CoursesGetWebController extends WebController { - public function __invoke(Request $request): Response - { - /** @var CoursesCounterResponse $coursesCounterResponse */ - $coursesCounterResponse = $this->ask(new FindCoursesCounterQuery()); + public function __invoke(Request $request): Response + { + /** @var CoursesCounterResponse $coursesCounterResponse */ + $coursesCounterResponse = $this->ask(new FindCoursesCounterQuery()); - return $this->render( - 'pages/courses/courses.html.twig', - [ - 'title' => 'Courses', - 'description' => 'Courses CodelyTV - Backoffice', - 'courses_counter' => $coursesCounterResponse->total(), - 'new_course_id' => Uuid::random()->value(), - ] - ); - } + 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 []; - } + 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 index 0b76138b7..625d2a1d0 100644 --- a/apps/backoffice/frontend/src/Controller/Courses/CoursesPostWebController.php +++ b/apps/backoffice/frontend/src/Controller/Courses/CoursesPostWebController.php @@ -14,48 +14,48 @@ final class CoursesPostWebController extends WebController { - public function __invoke(Request $request): RedirectResponse - { - $validationErrors = $this->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')) - ); - } + public function __invoke(Request $request): RedirectResponse + { + $validationErrors = $this->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 index 19c48acb4..a38a47d39 100644 --- a/apps/backoffice/frontend/src/Controller/HealthCheck/HealthCheckGetController.php +++ b/apps/backoffice/frontend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -9,12 +9,12 @@ final class HealthCheckGetController { - public function __invoke(Request $request): JsonResponse - { - return new JsonResponse( - [ - 'backoffice-frontend' => 'ok', - ] - ); - } + public function __invoke(Request $request): JsonResponse + { + return new JsonResponse( + [ + 'backoffice-frontend' => 'ok', + ] + ); + } } diff --git a/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php b/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php index 8cdc343d8..30e011847 100644 --- a/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php +++ b/apps/backoffice/frontend/src/Controller/Home/HomeGetWebController.php @@ -10,19 +10,16 @@ final class HomeGetWebController extends WebController { - public function __invoke(Request $request): Response - { - return $this->render( - 'pages/home.html.twig', - [ - 'title' => 'Welcome', - 'description' => 'CodelyTV - Backoffice', - ] - ); - } + public function __invoke(Request $request): Response + { + return $this->render('pages/home.html.twig', [ + 'title' => 'Welcome', + 'description' => 'CodelyTV - Backoffice', + ]); + } - protected function exceptions(): array - { - return []; - } + 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 index 0b194cf1d..a10a699ea 100644 --- a/apps/backoffice/frontend/src/Controller/Metrics/MetricsController.php +++ b/apps/backoffice/frontend/src/Controller/Metrics/MetricsController.php @@ -9,17 +9,15 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -final class MetricsController +final readonly class MetricsController { - public function __construct(private PrometheusMonitor $monitor) - { - } + public function __construct(private PrometheusMonitor $monitor) {} - public function __invoke(Request $request): Response - { - $renderer = new RenderTextFormat(); - $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); + public function __invoke(Request $request): Response + { + $renderer = new RenderTextFormat(); + $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); - return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); - } + return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); + } } 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 index f9717d54c..8edbfc3b1 100644 --- a/apps/bootstrap.php +++ b/apps/bootstrap.php @@ -1,28 +1,17 @@ =1.2) -if (is_array($env = @include $rootPath . '/.env.local.php')) { - foreach ($env as $k => $v) { - $_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && str_starts_with($k, 'HTTP_') ? $_SERVER[$k] : $v); - } -} elseif (!class_exists(Dotenv::class)) { - throw new RuntimeException( - 'Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.' - ); -} else { - // load all the .env files - (new Dotenv(false))->loadEnv($rootPath . '/.env'); -} +(new Dotenv())->loadEnv($rootPath . '/.env'); -$_SERVER += $_ENV; -$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; -$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_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'; + (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 index c794905a3..d013ee7a8 100755 --- a/apps/mooc/backend/bin/console +++ b/apps/mooc/backend/bin/console @@ -12,7 +12,7 @@ if (false === in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { set_time_limit(0); -require dirname(__DIR__) . '../../../vendor/autoload.php'; +require dirname(__DIR__) . '/../../../vendor/autoload.php'; if (!class_exists(Application::class)) { throw new RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.'); diff --git a/apps/mooc/backend/config/bundles.php b/apps/mooc/backend/config/bundles.php index 7de452252..15c297c9f 100644 --- a/apps/mooc/backend/config/bundles.php +++ b/apps/mooc/backend/config/bundles.php @@ -1,7 +1,9 @@ ['all' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], - // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] + Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], + // WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true] ]; diff --git a/apps/mooc/backend/public/index.php b/apps/mooc/backend/public/index.php index 9d02406aa..b8655fe09 100644 --- a/apps/mooc/backend/public/index.php +++ b/apps/mooc/backend/public/index.php @@ -1,5 +1,7 @@ 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 index 69aeaff18..16d96c2a6 100644 --- a/apps/mooc/backend/src/Command/DomainEvents/MySql/ConsumeMySqlDomainEventsCommand.php +++ b/apps/mooc/backend/src/Command/DomainEvents/MySql/ConsumeMySqlDomainEventsCommand.php @@ -8,50 +8,49 @@ use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator; use CodelyTv\Shared\Infrastructure\Bus\Event\MySql\MySqlDoctrineDomainEventsConsumer; use CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; + use function Lambdish\Phunctional\pipe; +#[AsCommand(name: 'codely:domain-events:mysql:consume', description: 'Consume domain events from MySql',)] final class ConsumeMySqlDomainEventsCommand extends Command { - protected static $defaultName = 'codelytv:domain-events:mysql:consume'; - - public function __construct( - private MySqlDoctrineDomainEventsConsumer $consumer, - private DatabaseConnections $connections, - private DomainEventSubscriberLocator $subscriberLocator - ) { - parent::__construct(); - } - - protected function configure(): void - { - $this - ->setDescription('Consume domain events from MySql') - ->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); - } - }; - } + public function __construct( + private readonly MySqlDoctrineDomainEventsConsumer $consumer, + private readonly DatabaseConnections $connections, + private readonly DomainEventSubscriberLocator $subscriberLocator + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this->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 index 3055c60a6..72801af71 100644 --- a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConfigureRabbitMqCommand.php +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConfigureRabbitMqCommand.php @@ -5,32 +5,30 @@ namespace CodelyTv\Apps\Mooc\Backend\Command\DomainEvents\RabbitMq; use CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqConfigurer; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Traversable; +#[AsCommand( + name: 'codely:domain-events:rabbitmq:configure', + description: 'Configure the RabbitMQ to allow publish & consume domain events', +)] final class ConfigureRabbitMqCommand extends Command { - protected static $defaultName = 'codelytv:domain-events:rabbitmq:configure'; - - public function __construct( - private RabbitMqConfigurer $configurer, - private string $exchangeName, - private Traversable $subscribers - ) { - parent::__construct(); - } - - protected function configure(): void - { - $this->setDescription('Configure the RabbitMQ to allow publish & consume domain events'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $this->configurer->configure($this->exchangeName, ...iterator_to_array($this->subscribers)); - - return 0; - } + public function __construct( + private readonly RabbitMqConfigurer $configurer, + private readonly string $exchangeName, + private readonly Traversable $subscribers + ) { + parent::__construct(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->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 index 4fcd6d403..c29c056d8 100644 --- a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConsumeRabbitMqDomainEventsCommand.php +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/ConsumeRabbitMqDomainEventsCommand.php @@ -7,50 +7,53 @@ use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator; use CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqDomainEventsConsumer; use CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; + use function Lambdish\Phunctional\repeat; +#[AsCommand( + name: 'codely:domain-events:rabbitmq:consume', + description: 'Consume domain events from the RabbitMQ', +)] final class ConsumeRabbitMqDomainEventsCommand extends Command { - protected static $defaultName = 'codelytv:domain-events:rabbitmq:consume'; - - public function __construct( - private RabbitMqDomainEventsConsumer $consumer, - private DatabaseConnections $connections, - private DomainEventSubscriberLocator $locator - ) { - parent::__construct(); - } - - protected function configure(): void - { - $this - ->setDescription('Consume domain events from the RabbitMQ') - ->addArgument('queue', InputArgument::REQUIRED, 'Queue name') - ->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of events to process'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $queueName = (string) $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(); - }; - } + public function __construct( + private readonly RabbitMqDomainEventsConsumer $consumer, + private readonly DatabaseConnections $connections, + private readonly DomainEventSubscriberLocator $locator + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->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 index 3fd739cbf..646392bf3 100644 --- a/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/GenerateSupervisorRabbitMqConsumerFilesCommand.php +++ b/apps/mooc/backend/src/Command/DomainEvents/RabbitMq/GenerateSupervisorRabbitMqConsumerFilesCommand.php @@ -7,73 +7,70 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventSubscriberLocator; use CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqQueueNameFormatter; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; + use function Lambdish\Phunctional\each; +#[AsCommand( + name: 'codely:domain-events:rabbitmq:generate-supervisor-files', + description: 'Generate the supervisor configuration for every RabbitMQ subscriber', +)] final class GenerateSupervisorRabbitMqConsumerFilesCommand extends Command { - private const EVENTS_TO_PROCESS_AT_TIME = 200; - private const NUMBERS_OF_PROCESSES_PER_SUBSCRIBER = 1; - private const SUPERVISOR_PATH = __DIR__ . '/../../../../build/supervisor'; - protected static $defaultName = 'codelytv:domain-events:rabbitmq:generate-supervisor-files'; + private const EVENTS_TO_PROCESS_AT_TIME = 200; + private const NUMBERS_OF_PROCESSES_PER_SUBSCRIBER = 1; + private const SUPERVISOR_PATH = __DIR__ . '/../../../../build/supervisor'; - public function __construct(private DomainEventSubscriberLocator $locator) - { - parent::__construct(); - } + public function __construct(private readonly DomainEventSubscriberLocator $locator) + { + parent::__construct(); + } - protected function configure(): void - { - $this - ->setDescription('Generate the supervisor configuration for every RabbitMQ subscriber') - ->addArgument('command-path', InputArgument::OPTIONAL, 'Path on this is gonna be deployed', '/var/www'); - } + protected function configure(): void + { + $this->addArgument('command-path', InputArgument::OPTIONAL, 'Path on this is gonna be deployed', '/var/www'); + } - protected function execute(InputInterface $input, OutputInterface $output): int - { - $path = (string) $input->getArgument('command-path'); + protected function execute(InputInterface $input, OutputInterface $output): int + { + $path = $input->getArgument('command-path'); - each($this->configCreator($path), $this->locator->all()); + each($this->configCreator($path), $this->locator->all()); - return 0; - } + return 0; + } - private function configCreator(string $path): callable - { - return function (DomainEventSubscriber $subscriber) use ($path): void { - $queueName = RabbitMqQueueNameFormatter::format($subscriber); - $subscriberName = RabbitMqQueueNameFormatter::shortFormat($subscriber); + 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() - ); + $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); - }; - } + 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') - ) - ); + public function __invoke(string $id, Request $request): Response + { + $this->dispatch( + new CreateCourseCommand( + $id, + (string) $request->request->get('name'), + (string) $request->request->get('duration') + ) + ); - return new Response('', Response::HTTP_CREATED); - } + return new Response('', Response::HTTP_CREATED); + } - protected function exceptions(): array - { - return []; - } + 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 index 22df95571..0fb94b6ff 100644 --- a/apps/mooc/backend/src/Controller/CoursesCounter/CoursesCounterGetController.php +++ b/apps/mooc/backend/src/Controller/CoursesCounter/CoursesCounterGetController.php @@ -13,22 +13,22 @@ final class CoursesCounterGetController extends ApiController { - public function __invoke(): JsonResponse - { - /** @var CoursesCounterResponse $response */ - $response = $this->ask(new FindCoursesCounterQuery()); + public function __invoke(): JsonResponse + { + /** @var CoursesCounterResponse $response */ + $response = $this->ask(new FindCoursesCounterQuery()); - return new JsonResponse( - [ - 'total' => $response->total(), - ] - ); - } + return new JsonResponse( + [ + 'total' => $response->total(), + ] + ); + } - protected function exceptions(): array - { - return [ - CoursesCounterNotExist::class => Response::HTTP_NOT_FOUND, - ]; - } + 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 index 82c30e36f..1c20feae2 100644 --- a/apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php +++ b/apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php @@ -8,19 +8,17 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -final class HealthCheckGetController +final readonly class HealthCheckGetController { - public function __construct(private RandomNumberGenerator $generator) - { - } + public function __construct(private RandomNumberGenerator $generator) {} - public function __invoke(Request $request): JsonResponse - { - return new JsonResponse( - [ - 'mooc-backend' => 'ok', - 'rand' => $this->generator->generate(), - ] - ); - } + public function __invoke(Request $request): JsonResponse + { + return new JsonResponse( + [ + 'mooc-backend' => '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 index 32428afd7..9db1cebc2 100644 --- a/apps/mooc/backend/src/Controller/Metrics/MetricsController.php +++ b/apps/mooc/backend/src/Controller/Metrics/MetricsController.php @@ -9,17 +9,15 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -final class MetricsController +final readonly class MetricsController { - public function __construct(private PrometheusMonitor $monitor) - { - } + public function __construct(private PrometheusMonitor $monitor) {} - public function __invoke(Request $request): Response - { - $renderer = new RenderTextFormat(); - $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); + public function __invoke(Request $request): Response + { + $renderer = new RenderTextFormat(); + $result = $renderer->render($this->monitor->registry()->getMetricFamilySamples()); - return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]); - } + 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 index 4ae0b228b..e0b82d39e 100644 --- a/apps/mooc/backend/src/MoocBackendKernel.php +++ b/apps/mooc/backend/src/MoocBackendKernel.php @@ -9,37 +9,38 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; + use function dirname; -final class MoocBackendKernel extends Kernel +class MoocBackendKernel extends Kernel { - use MicroKernelTrait; - - private const CONFIG_EXTS = '.{xml,yaml}'; - - public function registerBundles(): iterable - { - $contents = require $this->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'); - } + use MicroKernelTrait; + + private const string CONFIG_EXTS = '.{xml,yaml}'; + + public function registerBundles(): iterable + { + $contents = require $this->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/var/.gitkeep b/apps/mooc/backend/var/.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/composer.json b/composer.json index ea800fcce..52f696576 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "project", "description": "An example project applying Domain-Driven Design, Hexagonal Architecture and CQRS in a Monorepository", "require": { - "php": "^8.1", + "php": "^8.3", "ext-amqp": "*", "ext-apcu": "*", @@ -12,13 +12,13 @@ "ext-zend-opcache": "*", "ext-pdo": "*", - "symfony/framework-bundle": "^6", - "symfony/messenger": "^6", - "symfony/dotenv": "^6", - "symfony/yaml": "^6", - "symfony/twig-bundle": "^6", - "symfony/validator": "^6", - "symfony/cache": "^6", + "symfony/framework-bundle": "^7", + "symfony/messenger": "^7", + "symfony/dotenv": "^7", + "symfony/yaml": "^7", + "symfony/twig-bundle": "^7", + "symfony/validator": "^7", + "symfony/cache": "^7", "lambdish/phunctional": "^2", @@ -33,26 +33,35 @@ "elasticsearch/elasticsearch": "^7", "monolog/monolog": "^3", - "endclothing/prometheus_client_php": "^1" + "promphp/prometheus_client_php": "^2.7.2" }, "require-dev": { "ext-xdebug": "*", "roave/security-advisories": "dev-master", - "behat/behat": "^3", - "friends-of-behat/mink-extension": "^2", - "friends-of-behat/symfony-extension": "^2", - "behat/mink-browserkit-driver": "^2", + "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", "phpunit/phpunit": "^9", "mockery/mockery": "^1", "fakerphp/faker": "^1", - "symfony/error-handler": "^6", - "vimeo/psalm": "^4", - "friendsofphp/php-cs-fixer": "^3.8" + "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": { diff --git a/composer.lock b/composer.lock index f12724bfe..534d648f6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,30 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c096ab9ab7ca0c43b0244f7dcbb55400", + "content-hash": "1384ca0a67984f7a0296f15a4373fed1", "packages": [ { "name": "brick/math", - "version": "0.9.3", + "version": "0.12.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", - "vimeo/psalm": "4.9.2" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", "autoload": { @@ -47,24 +46,25 @@ "arithmetic", "bigdecimal", "bignum", + "bignumber", "brick", - "math" + "decimal", + "integer", + "math", + "mathematics", + "rational" ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.9.3" + "source": "https://github.com/brick/math/tree/0.12.1" }, "funding": [ { "url": "https://github.com/BenMorel", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/brick/math", - "type": "tidelift" } ], - "time": "2021-08-15T20:50:18+00:00" + "time": "2023-11-29T23:19:16+00:00" }, { "name": "doctrine/cache", @@ -161,31 +161,34 @@ }, { "name": "doctrine/collections", - "version": "1.6.8", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "1958a744696c6bb3bb0d28db2611dc11610e78af" + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/1958a744696c6bb3bb0d28db2611dc11610e78af", - "reference": "1958a744696c6bb3bb0d28db2611dc11610e78af", + "url": "https://api.github.com/repos/doctrine/collections/zipball/d8af7f248c74f195f7347424600fd9e17b57af59", + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59", "shasum": "" }, "require": { - "php": "^7.1.3 || ^8.0" + "doctrine/deprecations": "^1", + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.1.5", - "vimeo/psalm": "^4.2.1" + "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", "autoload": { "psr-4": { - "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" + "Doctrine\\Common\\Collections\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -224,22 +227,36 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/1.6.8" + "source": "https://github.com/doctrine/collections/tree/2.2.2" }, - "time": "2021-08-10T18:51:53+00:00" + "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": "3.3.0", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "c824e95d4c83b7102d8bc60595445a6f7d540f96" + "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/c824e95d4c83b7102d8bc60595445a6f7d540f96", - "reference": "c824e95d4c83b7102d8bc60595445a6f7d540f96", + "url": "https://api.github.com/repos/doctrine/common/zipball/0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", + "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", "shasum": "" }, "require": { @@ -247,18 +264,19 @@ "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9.0", + "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", "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -300,7 +318,7 @@ ], "support": { "issues": "https://github.com/doctrine/common/issues", - "source": "https://github.com/doctrine/common/tree/3.3.0" + "source": "https://github.com/doctrine/common/tree/3.4.4" }, "funding": [ { @@ -316,42 +334,44 @@ "type": "tidelift" } ], - "time": "2022-02-05T18:28:51+00:00" + "time": "2024-04-16T13:35:33+00:00" }, { "name": "doctrine/dbal", - "version": "3.3.6", + "version": "3.8.6", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "9e7f76dd1cde81c62574fdffa5a9c655c847ad21" + "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/9e7f76dd1cde81c62574fdffa5a9c655c847ad21", - "reference": "9e7f76dd1cde81c62574fdffa5a9c655c847ad21", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/b7411825cf7efb7e51f9791dea19d86e43b399a1", + "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1", "shasum": "" }, "require": { "composer-runtime-api": "^2", "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0", + "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": "9.0.0", - "jetbrains/phpstorm-stubs": "2022.1", - "phpstan/phpstan": "1.6.3", - "phpstan/phpstan-strict-rules": "^1.2", - "phpunit/phpunit": "9.5.20", - "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.2", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.23.0" + "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." @@ -411,7 +431,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.3.6" + "source": "https://github.com/doctrine/dbal/tree/3.8.6" }, "funding": [ { @@ -427,29 +447,33 @@ "type": "tidelift" } ], - "time": "2022-05-02T17:21:01+00:00" + "time": "2024-06-19T10:38:17+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "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" @@ -468,43 +492,40 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/event-manager", - "version": "1.1.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "conflict": { - "doctrine/common": "<2.9@dev" + "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^6.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/", @@ -548,7 +569,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" }, "funding": [ { @@ -564,32 +585,32 @@ "type": "tidelift" } ], - "time": "2020-05-29T18:28:51+00:00" + "time": "2024-05-22T20:47:39+00:00" }, { "name": "doctrine/inflector", - "version": "2.0.4", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", - "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "vimeo/psalm": "^4.10" + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", "autoload": { @@ -639,7 +660,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.4" + "source": "https://github.com/doctrine/inflector/tree/2.0.10" }, "funding": [ { @@ -655,34 +676,34 @@ "type": "tidelift" } ], - "time": "2021-10-22T20:16:43+00:00" + "time": "2024-02-18T20:23:39+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "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", "autoload": { @@ -709,7 +730,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -725,35 +746,36 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "1.2.3", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -785,7 +807,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -801,57 +823,59 @@ "type": "tidelift" } ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "doctrine/orm", - "version": "2.12.2", + "version": "2.19.6", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "8291a7f09b12d14783ed6537b4586583d155869e" + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/8291a7f09b12d14783ed6537b4586583d155869e", - "reference": "8291a7f09b12d14783ed6537b4586583d155869e", + "url": "https://api.github.com/repos/doctrine/orm/zipball/c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", "shasum": "" }, "require": { "composer-runtime-api": "^2", "doctrine/cache": "^1.12.1 || ^2.1.1", - "doctrine/collections": "^1.5", + "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.1", + "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", - "doctrine/instantiator": "^1.3", - "doctrine/lexer": "^1.2.3", + "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": "^3.0 || ^4.0 || ^5.0 || ^6.0", + "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 || >= 2.0" + "doctrine/annotations": "<1.13 || >= 3.0" }, "require-dev": { - "doctrine/annotations": "^1.13", - "doctrine/coding-standard": "^9.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.6.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "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.6.2", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.23.0" + "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" }, @@ -861,7 +885,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\ORM\\": "lib/Doctrine/ORM" + "Doctrine\\ORM\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -898,45 +922,41 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.12.2" + "source": "https://github.com/doctrine/orm/tree/2.19.6" }, - "time": "2022-05-02T19:10:07+00:00" + "time": "2024-06-26T17:24:40+00:00" }, { "name": "doctrine/persistence", - "version": "3.0.2", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "25ec98a8cbd1f850e60fdb62c0ef77c162da8704" + "reference": "b337726451f5d530df338fc7f68dee8781b49779" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/25ec98a8cbd1f850e60fdb62c0ef77c162da8704", - "reference": "25ec98a8cbd1f850e60fdb62c0ef77c162da8704", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/b337726451f5d530df338fc7f68dee8781b49779", + "reference": "b337726451f5d530df338fc7f68dee8781b49779", "shasum": "" }, "require": { - "doctrine/collections": "^1.0", - "doctrine/event-manager": "^1.0", + "doctrine/event-manager": "^1 || ^2", "php": "^7.2 || ^8.0", "psr/cache": "^1.0 || ^2.0 || ^3.0" }, "conflict": { - "doctrine/annotations": "<1.7 || >=2.0", "doctrine/common": "<2.10" }, "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^1.7", - "doctrine/coding-standard": "^9.0", + "doctrine/coding-standard": "^12", "doctrine/common": "^3.0", - "phpstan/phpstan": "1.5.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.22.0" + "vimeo/psalm": "4.30.0 || 5.24.0" }, "type": "library", "autoload": { @@ -985,7 +1005,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.0.2" + "source": "https://github.com/doctrine/persistence/tree/3.3.3" }, "funding": [ { @@ -1001,20 +1021,20 @@ "type": "tidelift" } ], - "time": "2022-05-06T06:10:05+00:00" + "time": "2024-06-20T10:14:30+00:00" }, { "name": "elasticsearch/elasticsearch", - "version": "v7.17.0", + "version": "v7.17.2", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "1890f9d7fde076b5a3ddcf579a802af05b2e781b" + "reference": "2d302233f2bb0926812d82823bb820d405e130fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/1890f9d7fde076b5a3ddcf579a802af05b2e781b", - "reference": "1890f9d7fde076b5a3ddcf579a802af05b2e781b", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/2d302233f2bb0926812d82823bb820d405e130fc", + "reference": "2d302233f2bb0926812d82823bb820d405e130fc", "shasum": "" }, "require": { @@ -1027,7 +1047,7 @@ "ext-yaml": "*", "ext-zip": "*", "mockery/mockery": "^1.2", - "phpstan/phpstan": "^0.12", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.4", "symfony/finder": "~4.0" @@ -1066,78 +1086,29 @@ ], "support": { "issues": "https://github.com/elastic/elasticsearch-php/issues", - "source": "https://github.com/elastic/elasticsearch-php/tree/v7.17.0" - }, - "time": "2022-02-03T13:40:04+00:00" - }, - { - "name": "endclothing/prometheus_client_php", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/endclothing/prometheus_client_php.git", - "reference": "ae61369d8667343cfff70fec648b3448a2076778" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/endclothing/prometheus_client_php/zipball/ae61369d8667343cfff70fec648b3448a2076778", - "reference": "ae61369d8667343cfff70fec648b3448a2076778", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/guzzle": "^6.2", - "php": "^7.1", - "symfony/polyfill-apcu": "^1.6" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-apc": "Required if using APCu.", - "ext-redis": "Required if using Redis." + "source": "https://github.com/elastic/elasticsearch-php/tree/v7.17.2" }, - "type": "library", - "autoload": { - "psr-4": { - "Prometheus\\": "src/Prometheus/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Daniel Noel-Davies", - "email": "Daniel.Noel-Davies@endclothing.com" - } - ], - "support": { - "issues": "https://github.com/endclothing/prometheus_client_php/issues", - "source": "https://github.com/endclothing/prometheus_client_php/tree/v1.0.1" - }, - "time": "2019-10-04T10:46:29+00:00" + "time": "2023-04-21T15:31:12+00:00" }, { "name": "ezimuel/guzzlestreams", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/ezimuel/guzzlestreams.git", - "reference": "abe3791d231167f14eb80d413420d1eab91163a8" + "reference": "b4b5a025dfee70d6cd34c780e07330eb93d5b997" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezimuel/guzzlestreams/zipball/abe3791d231167f14eb80d413420d1eab91163a8", - "reference": "abe3791d231167f14eb80d413420d1eab91163a8", + "url": "https://api.github.com/repos/ezimuel/guzzlestreams/zipball/b4b5a025dfee70d6cd34c780e07330eb93d5b997", + "reference": "b4b5a025dfee70d6cd34c780e07330eb93d5b997", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~9.0" }, "type": "library", "extra": { @@ -1168,22 +1139,22 @@ "stream" ], "support": { - "source": "https://github.com/ezimuel/guzzlestreams/tree/3.0.1" + "source": "https://github.com/ezimuel/guzzlestreams/tree/3.1.0" }, - "time": "2020-02-14T23:11:50+00:00" + "time": "2022-10-24T12:58:50+00:00" }, { "name": "ezimuel/ringphp", - "version": "1.2.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/ezimuel/ringphp.git", - "reference": "92b8161404ab1ad84059ebed41d9f757e897ce74" + "reference": "7887fc8488013065f72f977dcb281994f5fde9f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezimuel/ringphp/zipball/92b8161404ab1ad84059ebed41d9f757e897ce74", - "reference": "92b8161404ab1ad84059ebed41d9f757e897ce74", + "url": "https://api.github.com/repos/ezimuel/ringphp/zipball/7887fc8488013065f72f977dcb281994f5fde9f4", + "reference": "7887fc8488013065f72f977dcb281994f5fde9f4", "shasum": "" }, "require": { @@ -1225,51 +1196,36 @@ ], "description": "Fork of guzzle/RingPHP (abandoned) to be used with elasticsearch-php", "support": { - "source": "https://github.com/ezimuel/ringphp/tree/1.2.0" + "source": "https://github.com/ezimuel/ringphp/tree/1.2.2" }, - "time": "2021-11-16T11:51:30+00:00" + "time": "2022-12-07T11:28:53+00:00" }, { - "name": "friendsofphp/proxy-manager-lts", - "version": "v1.0.12", + "name": "lambdish/phunctional", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git", - "reference": "8419f0158715b30d4b99a5bd37c6a39671994ad7" + "url": "https://github.com/Lambdish/phunctional.git", + "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/8419f0158715b30d4b99a5bd37c6a39671994ad7", - "reference": "8419f0158715b30d4b99a5bd37c6a39671994ad7", + "url": "https://api.github.com/repos/Lambdish/phunctional/zipball/ed3482e7da134d886789abb33c6df22a5d2f271c", + "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c", "shasum": "" }, "require": { - "laminas/laminas-code": "~3.4.1|^4.0", - "php": ">=7.1", - "symfony/filesystem": "^4.4.17|^5.0|^6.0" - }, - "conflict": { - "laminas/laminas-stdlib": "<3.2.1", - "zendframework/zend-stdlib": "<3.2.1" - }, - "replace": { - "ocramius/proxy-manager": "^2.1" + "php": ">=7.2" }, "require-dev": { - "ext-phar": "*", - "symfony/phpunit-bridge": "^5.4|^6.0" + "phpstan/phpstan": "^0.11.16", + "phpunit/phpunit": "^8.4" }, "type": "library", - "extra": { - "thanks": { - "name": "ocramius/proxy-manager", - "url": "https://github.com/Ocramius/ProxyManager" - } - }, "autoload": { - "psr-4": { - "ProxyManager\\": "src/ProxyManager" - } + "files": [ + "src/_bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1277,280 +1233,220 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Eloi Poch" }, { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Jorge Ávila" + }, + { + "name": "Rafa GΓ³mez" } ], - "description": "Adding support for a wider range of PHP versions to ocramius/proxy-manager", - "homepage": "https://github.com/FriendsOfPHP/proxy-manager-lts", + "description": "Ξ» PHP functional library", "keywords": [ - "aop", - "lazy loading", - "proxy", - "proxy pattern", - "service proxies" + "functional", + "generator", + "lambda", + "library", + "php" ], "support": { - "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues", - "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.12" + "issues": "https://github.com/Lambdish/phunctional/issues", + "source": "https://github.com/Lambdish/phunctional/tree/v2.1.0" }, - "funding": [ - { - "url": "https://github.com/Ocramius", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", - "type": "tidelift" - } - ], - "time": "2022-05-05T09:31:05+00:00" + "time": "2020-09-18T07:22:08+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "6.5.7", + "name": "laminas/laminas-code", + "version": "4.14.0", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "724562fa861e21a4071c652c8a159934e4f05592" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/724562fa861e21a4071c652c8a159934e4f05592", - "reference": "724562fa861e21a4071c652c8a159934e4f05592", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/562e02b7d85cb9142b5116cc76c4c7c162a11a1c", + "reference": "562e02b7d85cb9142b5116cc76c4c7c162a11a1c", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.6.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.17.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.1" + "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" }, "suggest": { - "psr/log": "Required for using the Log middleware" + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5-dev" - } - }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "MΓ‘rk SΓ‘gi-KazΓ‘r", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } + "BSD-3-Clause" ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" + "code", + "laminas", + "laminasframework" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/6.5.7" + "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": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" } ], - "time": "2022-06-09T21:36:50+00:00" + "time": "2024-06-17T08:50:25+00:00" }, { - "name": "guzzlehttp/promises", - "version": "1.5.1", + "name": "laminas/laminas-zendframework-bridge", + "version": "1.8.0", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/eb0d96c708b92177a92bc2239543d3ed523452c6", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.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-dev" + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" } }, "autoload": { "files": [ - "src/functions_include.php" + "src/autoload.php" ], "psr-4": { - "GuzzleHttp\\Promise\\": "src/" + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } + "BSD-3-Clause" ], - "description": "Guzzle promises library", + "description": "Alias legacy ZF class names to Laminas Project equivalents.", "keywords": [ - "promise" + "ZendFramework", + "autoloading", + "laminas", + "zf" ], "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.1" + "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": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" } ], - "time": "2021-10-22T20:56:57+00:00" + "abandoned": true, + "time": "2023-11-24T13:56:19+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "1.8.5", + "name": "monolog/monolog", + "version": "3.7.0", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/337e3ad8e5716c15f9657bd214d16cc5e69df268", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/http-message-implementation": "1.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-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-main": "3.x-dev" + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", @@ -1559,353 +1455,182 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "MΓ‘rk SΓ‘gi-KazΓ‘r", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" + "log", + "logging", + "psr-3" ], "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.5" + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.7.0" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", + "url": "https://github.com/Seldaek", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", "type": "tidelift" } ], - "time": "2022-03-20T21:51:18+00:00" + "time": "2024-06-28T09:40:51+00:00" }, { - "name": "lambdish/phunctional", - "version": "v2.1.0", + "name": "ocramius/proxy-manager", + "version": "2.14.1", "source": { "type": "git", - "url": "https://github.com/Lambdish/phunctional.git", - "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c" + "url": "https://github.com/Ocramius/ProxyManager.git", + "reference": "3990d60ef79001badbab4927a6a811682274a0d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Lambdish/phunctional/zipball/ed3482e7da134d886789abb33c6df22a5d2f271c", - "reference": "ed3482e7da134d886789abb33c6df22a5d2f271c", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/3990d60ef79001badbab4927a6a811682274a0d1", + "reference": "3990d60ef79001badbab4927a6a811682274a0d1", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "require-dev": { - "phpstan/phpstan": "^0.11.16", - "phpunit/phpunit": "^8.4" + "composer-runtime-api": "^2.1.0", + "laminas/laminas-code": "^4.4.2", + "php": "~8.0.0", + "webimpress/safe-writer": "^2.2.0" }, - "type": "library", - "autoload": { - "files": [ - "src/_bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eloi Poch" - }, - { - "name": "Jorge Ávila" - }, - { - "name": "Rafa GΓ³mez" - } - ], - "description": "Ξ» PHP functional library", - "keywords": [ - "functional", - "generator", - "lambda", - "library", - "php" - ], - "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": "laminas/laminas-code", - "version": "4.5.2", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-code.git", - "reference": "da01fb74c08f37e20e7ae49f1e3ee09aa401ebad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/da01fb74c08f37e20e7ae49f1e3ee09aa401ebad", - "reference": "da01fb74c08f37e20e7ae49f1e3ee09aa401ebad", - "shasum": "" - }, - "require": { - "php": ">=7.4, <8.2" + "conflict": { + "thecodingmachine/safe": "<1.3.3" }, "require-dev": { - "doctrine/annotations": "^1.13.2", + "codelicia/xulieta": "^0.1.6", + "doctrine/coding-standard": "^9.0.0", "ext-phar": "*", - "laminas/laminas-coding-standard": "^2.3.0", - "laminas/laminas-stdlib": "^3.6.1", - "phpunit/phpunit": "^9.5.10", - "psalm/plugin-phpunit": "^0.16.1", - "vimeo/psalm": "^4.13.1" + "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/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "laminas/laminas-stdlib": "Laminas\\Stdlib component" + "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", "autoload": { - "files": [ - "polyfill/ReflectionEnumPolyfill.php" - ], "psr-4": { - "Laminas\\Code\\": "src/" + "ProxyManager\\": "src/ProxyManager" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", - "homepage": "https://laminas.dev", - "keywords": [ - "code", - "laminas", - "laminasframework" + "MIT" ], - "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": [ + "authors": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" } ], - "time": "2022-06-06T11:26:02+00:00" - }, - { - "name": "laminas/laminas-zendframework-bridge", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "7f049390b756d34ba5940a8fb47634fbb51f79ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/7f049390b756d34ba5940a8fb47634fbb51f79ab", - "reference": "7f049390b756d34ba5940a8fb47634fbb51f79ab", - "shasum": "" - }, - "require": { - "php": ">=7.4, <8.2" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.14", - "psalm/plugin-phpunit": "^0.15.2", - "squizlabs/php_codesniffer": "^3.6.2", - "vimeo/psalm": "^4.21.0" - }, - "type": "library", - "extra": { - "laminas": { - "module": "Laminas\\ZendFrameworkBridge" - } - }, - "autoload": { - "files": [ - "src/autoload.php" - ], - "psr-4": { - "Laminas\\ZendFrameworkBridge\\": "src//" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Alias legacy ZF class names to Laminas Project equivalents.", + "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", + "homepage": "https://github.com/Ocramius/ProxyManager", "keywords": [ - "ZendFramework", - "autoloading", - "laminas", - "zf" + "aop", + "lazy loading", + "proxy", + "proxy pattern", + "service proxies" ], "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" + "issues": "https://github.com/Ocramius/ProxyManager/issues", + "source": "https://github.com/Ocramius/ProxyManager/tree/2.14.1" }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", + "type": "tidelift" } ], - "time": "2022-02-22T22:17:01+00:00" + "time": "2022-03-05T18:43:14+00:00" }, { - "name": "monolog/monolog", - "version": "3.1.0", + "name": "promphp/prometheus_client_php", + "version": "v2.11.0", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "0c375495d40df0207e5833dca333f963b171ff43" + "url": "https://github.com/PromPHP/prometheus_client_php.git", + "reference": "35d5a68628ea18209938bc1b8796646015ab93cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/0c375495d40df0207e5833dca333f963b171ff43", - "reference": "0c375495d40df0207e5833dca333f963b171ff43", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/35d5a68628ea18209938bc1b8796646015ab93cf", + "reference": "35d5a68628ea18209938bc1b8796646015ab93cf", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/log": "^2.0 || ^3.0" + "ext-json": "*", + "php": "^7.2|^8.0" }, - "provide": { - "psr/log-implementation": "3.0.0" + "replace": { + "endclothing/prometheus_client_php": "*", + "jimdo/prometheus_client_php": "*", + "lkaemmerling/prometheus_client_php": "*" }, "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", - "guzzlehttp/guzzle": "^7.4", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.3", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^9.5.16", - "predis/predis": "^1.1", - "ruflin/elastica": "^7", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" + "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": { - "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", - "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" + "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": "library", "extra": { "branch-alias": { - "dev-main": "3.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Prometheus\\": "src/Prometheus/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "Lukas KΓ€mmerling", + "email": "kontakt@lukas-kaemmerling.de" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], + "description": "Prometheus instrumentation library for PHP applications.", "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.1.0" + "issues": "https://github.com/PromPHP/prometheus_client_php/issues", + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.11.0" }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2022-06-09T09:09:00+00:00" + "time": "2024-08-05T07:58:08+00:00" }, { "name": "psr/cache", @@ -1957,26 +1682,26 @@ "time": "2021-02-03T23:26:27+00:00" }, { - "name": "psr/container", - "version": "1.1.2", + "name": "psr/clock", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": "^7.0 || ^8.0" }, "type": "library", "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Psr\\Clock\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1989,47 +1714,47 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "clock", + "now", + "psr", + "psr-20", + "time" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2022-11-25T14:36:26+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2039,37 +1764,40 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Standard interfaces for event handling.", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "events", - "psr", - "psr-14" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", "extra": { @@ -2079,7 +1807,7 @@ }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2092,20 +1820,17 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Standard interfaces for event handling.", "keywords": [ - "http", - "http-message", + "events", "psr", - "psr-7", - "request", - "response" + "psr-14" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { "name": "psr/log", @@ -2149,96 +1874,62 @@ "homepage": "https://github.com/php-fig/log", "keywords": [ "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } + "psr", + "psr-3" ], - "description": "A polyfill for getallheaders.", "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2019-03-08T08:55:37+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "ramsey/collection", - "version": "1.2.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", - "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", "shasum": "" }, "require": { - "php": "^7.3 || ^8", - "symfony/polyfill-php81": "^1.23" + "php": "^8.1" }, "require-dev": { - "captainhook/captainhook": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.6", - "fakerphp/faker": "^1.5", - "hamcrest/hamcrest-php": "^2", - "jangregor/phpstan-prophecy": "^0.8", - "mockery/mockery": "^1.3", + "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", - "phpstan/phpstan": "^0.12.32", - "phpstan/phpstan-mockery": "^0.12.5", - "phpstan/phpstan-phpunit": "^0.12.11", - "phpunit/phpunit": "^8.5 || ^9", - "psy/psysh": "^0.10.4", - "slevomat/coding-standard": "^6.3", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.4" + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" }, "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, "autoload": { "psr-4": { "Ramsey\\Collection\\": "src/" @@ -2266,7 +1957,7 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.2.2" + "source": "https://github.com/ramsey/collection/tree/2.0.0" }, "funding": [ { @@ -2278,28 +1969,27 @@ "type": "tidelift" } ], - "time": "2021-10-10T03:01:02+00:00" + "time": "2022-12-31T21:50:55+00:00" }, { "name": "ramsey/uuid", - "version": "4.3.1", + "version": "4.7.6", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28" + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", - "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", "shasum": "" }, "require": { - "brick/math": "^0.8 || ^0.9", - "ext-ctype": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", "ext-json": "*", "php": "^8.0", - "ramsey/collection": "^1.0" + "ramsey/collection": "^1.2 || ^2.0" }, "replace": { "rhumsaa/uuid": "self.version" @@ -2311,24 +2001,23 @@ "doctrine/annotations": "^1.8", "ergebnis/composer-normalize": "^2.15", "mockery/mockery": "^1.3", - "moontoast/math": "^1.1", "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.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-mockery": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", "phpunit/phpunit": "^8.5 || ^9", - "slevomat/coding-standard": "^7.0", + "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-ctype": "Enables faster processing of character classification using ctype functions.", "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", @@ -2360,7 +2049,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.3.1" + "source": "https://github.com/ramsey/uuid/tree/4.7.6" }, "funding": [ { @@ -2372,27 +2061,27 @@ "type": "tidelift" } ], - "time": "2022-03-27T21:42:02+00:00" + "time": "2024-04-27T21:32:50+00:00" }, { "name": "react/promise", - "version": "v2.9.0", + "version": "v2.11.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910" + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/234f8fd1023c9158e2314fa9d7d0e6a83db42910", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910", + "url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { @@ -2436,47 +2125,44 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.9.0" + "source": "https://github.com/reactphp/promise/tree/v2.11.0" }, "funding": [ { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2022-02-11T10:27:51+00:00" + "time": "2023-11-16T16:16:50+00:00" }, { "name": "symfony/cache", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "364fc90734230d936ac2db8e897cc03ec8497bbb" + "reference": "8ac37acee794372f9732fe8a61a8221f6762148e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/364fc90734230d936ac2db8e897cc03ec8497bbb", - "reference": "364fc90734230d936ac2db8e897cc03ec8497bbb", + "url": "https://api.github.com/repos/symfony/cache/zipball/8ac37acee794372f9732fe8a61a8221f6762148e", + "reference": "8ac37acee794372f9732fe8a61a8221f6762148e", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^1.1.7|^2|^3", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/var-exporter": "^5.4|^6.0" + "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" }, "conflict": { - "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/var-dumper": "<5.4" + "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", @@ -2485,15 +2171,15 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^2.13.1|^3.0", - "predis/predis": "^1.1", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.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": { @@ -2521,14 +2207,14 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", "homepage": "https://symfony.com", "keywords": [ "caching", "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.1.1" + "source": "https://github.com/symfony/cache/tree/v7.1.3" }, "funding": [ { @@ -2544,33 +2230,30 @@ "type": "tidelift" } ], - "time": "2022-06-06T19:15:01+00:00" + "time": "2024-07-17T06:10:24+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.1.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "2eab7fa459af6d75c6463e63e633b667a9b761d3" + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/2eab7fa459af6d75c6463e63e633b667a9b761d3", - "reference": "2eab7fa459af6d75c6463e63e633b667a9b761d3", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197", "shasum": "" }, "require": { "php": ">=8.1", "psr/cache": "^3.0" }, - "suggest": { - "symfony/cache-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2607,7 +2290,81 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.1.0" + "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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", + "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.1.1" }, "funding": [ { @@ -2623,40 +2380,38 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/config", - "version": "v6.1.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "ed8d12417bcacd2d969750feb1fe1aab1c11e613" + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/ed8d12417bcacd2d969750feb1fe1aab1c11e613", - "reference": "ed8d12417bcacd2d969750feb1fe1aab1c11e613", + "url": "https://api.github.com/repos/symfony/config/zipball/2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^5.4|^6.0", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<5.4" + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^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": { @@ -2684,7 +2439,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.1.0" + "source": "https://github.com/symfony/config/tree/v7.1.1" }, "funding": [ { @@ -2700,53 +2455,50 @@ "type": "tidelift" } ], - "time": "2022-05-17T12:56:32+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/console", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6187424023fbffcd757789aeb517c9161b1eabee" + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6187424023fbffcd757789aeb517c9161b1eabee", - "reference": "6187424023fbffcd757789aeb517c9161b1eabee", + "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^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": { @@ -2775,12 +2527,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.1.1" + "source": "https://github.com/symfony/console/tree/v7.1.3" }, "funding": [ { @@ -2796,50 +2548,43 @@ "type": "tidelift" } ], - "time": "2022-06-08T14:02:09+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "fc1fcd2b153f585934e80055bb3254913def2a6e" + "reference": "8126f0be4ff984e4db0140e60917900a53facb49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/fc1fcd2b153f585934e80055bb3254913def2a6e", - "reference": "fc1fcd2b153f585934e80055bb3254913def2a6e", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8126f0be4ff984e4db0140e60917900a53facb49", + "reference": "8126f0be4ff984e4db0140e60917900a53facb49", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/service-contracts": "^1.1.6|^2.0|^3.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.5", + "symfony/var-exporter": "^6.4|^7.0" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<6.1", - "symfony/finder": "<5.4", - "symfony/proxy-manager-bridge": "<5.4", - "symfony/yaml": "<5.4" + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.1", - "symfony/expression-language": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.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": "" + "symfony/config": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -2867,7 +2612,7 @@ "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/v6.1.0" + "source": "https://github.com/symfony/dependency-injection/tree/v7.1.3" }, "funding": [ { @@ -2883,20 +2628,20 @@ "type": "tidelift" } ], - "time": "2022-05-27T06:40:20+00:00" + "time": "2024-07-26T07:35:39+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.1.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -2905,7 +2650,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2934,7 +2679,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -2950,31 +2695,32 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/dotenv", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "568c11bcedf419e7e61f663912c3547b54de51df" + "reference": "a26be30fd61678dab694a18a85084cea7673bbf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/568c11bcedf419e7e61f663912c3547b54de51df", - "reference": "568c11bcedf419e7e61f663912c3547b54de51df", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/a26be30fd61678dab694a18a85084cea7673bbf3", + "reference": "a26be30fd61678dab694a18a85084cea7673bbf3", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4", + "symfony/process": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" + "symfony/console": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3007,7 +2753,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v6.1.0" + "source": "https://github.com/symfony/dotenv/tree/v7.1.3" }, "funding": [ { @@ -3023,31 +2769,35 @@ "type": "tidelift" } ], - "time": "2022-04-01T07:15:35+00:00" + "time": "2024-07-09T19:36:07+00:00" }, { "name": "symfony/error-handler", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "d02c662651e5de760bb7d5e94437113309e8f8a0" + "reference": "432bb369952795c61ca1def65e078c4a80dad13c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/d02c662651e5de760bb7d5e94437113309e8f8a0", - "reference": "d02c662651e5de760bb7d5e94437113309e8f8a0", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c", + "reference": "432bb369952795c61ca1def65e078c4a80dad13c", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "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" @@ -3078,7 +2828,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.1.0" + "source": "https://github.com/symfony/error-handler/tree/v7.1.3" }, "funding": [ { @@ -3094,28 +2844,29 @@ "type": "tidelift" } ], - "time": "2022-05-23T10:32:57+00:00" + "time": "2024-07-26T13:02:51+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.1.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a0449a7ad7daa0f7c0acd508259f80544ab5a347" + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a0449a7ad7daa0f7c0acd508259f80544ab5a347", - "reference": "a0449a7ad7daa0f7c0acd508259f80544ab5a347", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2|^3" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", @@ -3123,17 +2874,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^5.4|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3161,7 +2908,7 @@ "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/v6.1.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" }, "funding": [ { @@ -3177,33 +2924,30 @@ "type": "tidelift" } ], - "time": "2022-05-05T16:51:07+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.1.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "02ff5eea2f453731cfbc6bc215e456b781480448" + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/02ff5eea2f453731cfbc6bc215e456b781480448", - "reference": "02ff5eea2f453731cfbc6bc215e456b781480448", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", "shasum": "" }, "require": { "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3240,7 +2984,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" }, "funding": [ { @@ -3256,27 +3000,30 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", - "version": "v6.1.0", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3132d2f43ca799c2aa099f9738d98228c56baa5d" + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3132d2f43ca799c2aa099f9738d98228c56baa5d", - "reference": "3132d2f43ca799c2aa099f9738d98228c56baa5d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -3303,7 +3050,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.1.0" + "source": "https://github.com/symfony/filesystem/tree/v7.1.2" }, "funding": [ { @@ -3319,27 +3066,27 @@ "type": "tidelift" } ], - "time": "2022-05-21T13:34:40+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { "name": "symfony/finder", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "45b8beb69d6eb3b05a65689ebfd4222326773f8f" + "reference": "717c6329886f32dc65e27461f80f2a465412fdca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/45b8beb69d6eb3b05a65689ebfd4222326773f8f", - "reference": "45b8beb69d6eb3b05a65689ebfd4222326773f8f", + "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", + "reference": "717c6329886f32dc65e27461f80f2a465412fdca", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3367,7 +3114,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.1.0" + "source": "https://github.com/symfony/finder/tree/v7.1.3" }, "funding": [ { @@ -3383,114 +3130,110 @@ "type": "tidelift" } ], - "time": "2022-04-15T08:08:08+00:00" + "time": "2024-07-24T07:08:44+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "260d97823252318eb3b525dd8c0bee2cc5dbfd7f" + "reference": "a32ec544bd501eb4619eb977860ad3076ee55061" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/260d97823252318eb3b525dd8c0bee2cc5dbfd7f", - "reference": "260d97823252318eb3b525dd8c0bee2cc5dbfd7f", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a32ec544bd501eb4619eb977860ad3076ee55061", + "reference": "a32ec544bd501eb4619eb977860ad3076ee55061", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.1", - "symfony/cache": "^5.4|^6.0", - "symfony/config": "^6.1", - "symfony/dependency-injection": "^6.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^6.1", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/http-kernel": "^6.1", + "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": "^5.4|^6.0" + "symfony/routing": "^6.4|^7.0" }, "conflict": { - "doctrine/annotations": "<1.13.1", "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "phpunit/phpunit": "<5.4.3", - "symfony/asset": "<5.4", - "symfony/console": "<5.4", - "symfony/dom-crawler": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/form": "<5.4", - "symfony/http-client": "<5.4", - "symfony/lock": "<5.4", - "symfony/mailer": "<5.4", - "symfony/messenger": "<5.4", - "symfony/mime": "<5.4", - "symfony/property-access": "<5.4", - "symfony/property-info": "<5.4", - "symfony/security-core": "<5.4", - "symfony/security-csrf": "<5.4", - "symfony/serializer": "<6.1", - "symfony/stopwatch": "<5.4", - "symfony/translation": "<5.4", - "symfony/twig-bridge": "<5.4", - "symfony/twig-bundle": "<5.4", - "symfony/validator": "<5.4", - "symfony/web-profiler-bundle": "<5.4", - "symfony/workflow": "<5.4" + "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": { - "doctrine/annotations": "^1.13.1", "doctrine/persistence": "^1.3|^2|^3", + "dragonmantank/cron-expression": "^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^5.4|^6.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/console": "^5.4.9|^6.0.9", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/dotenv": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/form": "^5.4|^6.0", - "symfony/html-sanitizer": "^6.1", - "symfony/http-client": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/mailer": "^5.4|^6.0", - "symfony/messenger": "^6.1", - "symfony/mime": "^5.4|^6.0", - "symfony/notifier": "^5.4|^6.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": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/rate-limiter": "^5.4|^6.0", - "symfony/security-bundle": "^5.4|^6.0", - "symfony/semaphore": "^5.4|^6.0", - "symfony/serializer": "^6.1", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/string": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/twig-bundle": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0", - "symfony/web-link": "^5.4|^6.0", - "symfony/workflow": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0", - "twig/twig": "^2.10|^3.0" - }, - "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" + "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": { @@ -3518,7 +3261,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.1.1" + "source": "https://github.com/symfony/framework-bundle/tree/v7.1.3" }, "funding": [ { @@ -3534,35 +3277,40 @@ "type": "tidelift" } ], - "time": "2022-06-09T10:53:06+00:00" + "time": "2024-07-26T13:24:34+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "a58dc88d56e04e57993d96c1407a17407610e1df" + "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a58dc88d56e04e57993d96c1407a17407610e1df", - "reference": "a58dc88d56e04e57993d96c1407a17407610e1df", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f602d5c17d1fa02f8019ace2687d9d136b7f4a1a", + "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0" + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4" }, - "suggest": { - "symfony/mime": "To use the file extension guesser" + "require-dev": { + "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", "autoload": { @@ -3590,7 +3338,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.1.1" + "source": "https://github.com/symfony/http-foundation/tree/v7.1.3" }, "funding": [ { @@ -3606,73 +3354,77 @@ "type": "tidelift" } ], - "time": "2022-05-31T14:28:03+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "86c4d6f6c5b6cd012df41e3b950c924b3ffdc019" + "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/86c4d6f6c5b6cd012df41e3b950c924b3ffdc019", - "reference": "86c4d6f6c5b6cd012df41e3b950c924b3ffdc019", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/db9702f3a04cc471ec8c70e881825db26ac5f186", + "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/error-handler": "^6.1", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.4", - "symfony/config": "<6.1", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.1", - "symfony/doctrine-bridge": "<5.4", - "symfony/form": "<5.4", - "symfony/http-client": "<5.4", - "symfony/mailer": "<5.4", - "symfony/messenger": "<5.4", - "symfony/translation": "<5.4", - "symfony/twig-bridge": "<5.4", - "symfony/validator": "<5.4", - "twig/twig": "<2.13" + "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" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^6.1", - "symfony/console": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.1", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", - "symfony/uid": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" + "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", "autoload": { @@ -3700,7 +3452,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.1.1" + "source": "https://github.com/symfony/http-kernel/tree/v7.1.3" }, "funding": [ { @@ -3716,211 +3468,58 @@ "type": "tidelift" } ], - "time": "2022-06-09T17:31:33+00:00" + "time": "2024-07-26T14:58:15+00:00" }, { "name": "symfony/messenger", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "e4e204ce4f2e90b54320c9043a0898d925dcd118" + "reference": "604e182a7758ceea35921a8ad5dd492a6e13bae4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/e4e204ce4f2e90b54320c9043a0898d925dcd118", - "reference": "e4e204ce4f2e90b54320c9043a0898d925dcd118", + "url": "https://api.github.com/repos/symfony/messenger/zipball/604e182a7758ceea35921a8ad5dd492a6e13bae4", + "reference": "604e182a7758ceea35921a8ad5dd492a6e13bae4", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/log": "^1|^2|^3" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/clock": "^6.4|^7.0" }, "conflict": { - "symfony/event-dispatcher": "<5.4", - "symfony/event-dispatcher-contracts": "<2", - "symfony/framework-bundle": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/serializer": "<5.4" + "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": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0" - }, - "suggest": { - "enqueue/messenger-adapter": "For using the php-enqueue library as a transport." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Samuel Roze", - "email": "samuel.roze@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps applications send and receive messages to/from other applications or via message queues", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/messenger/tree/v6.1.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": "2022-05-11T12:12:29+00:00" - }, - { - "name": "symfony/polyfill-apcu", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-apcu.git", - "reference": "43273a33c46f9d5a08dac76859f63d6814242e81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/43273a33c46f9d5a08dac76859f63d6814242e81", - "reference": "43273a33c46f9d5a08dac76859f63d6814242e81", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Apcu\\": "" - } - }, - "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 apcu_* functions to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "apcu", - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-apcu/tree/v1.26.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": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" + "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", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "Symfony\\Component\\Messenger\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3928,24 +3527,18 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Samuel Roze", + "email": "samuel.roze@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/messenger/tree/v7.1.3" }, "funding": [ { @@ -3961,33 +3554,33 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-07-09T19:36:07+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-ctype": "*" + }, "suggest": { - "ext-intl": "For best performance" + "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3998,7 +3591,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -4007,26 +3600,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "grapheme", - "intl", + "ctype", "polyfill", - "portable", - "shim" + "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -4042,35 +3633,30 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { - "name": "symfony/polyfill-intl-idn", - "version": "v1.26.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8", - "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4081,7 +3667,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -4090,30 +3676,26 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "idn", + "grapheme", "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -4129,20 +3711,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { @@ -4153,9 +3735,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4197,7 +3776,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -4213,20 +3792,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -4240,9 +3819,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4280,7 +3856,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -4296,20 +3872,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.26.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2" + "reference": "10112722600777e02d2745716b70c5db4ca70442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2", - "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", + "reference": "10112722600777e02d2745716b70c5db4ca70442", "shasum": "" }, "require": { @@ -4317,9 +3893,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4356,7 +3929,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" }, "funding": [ { @@ -4372,20 +3945,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.26.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { @@ -4393,9 +3966,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4439,7 +4009,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" }, "funding": [ { @@ -4455,20 +4025,20 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.26.0", + "name": "symfony/polyfill-php83", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", "shasum": "" }, "require": { @@ -4476,9 +4046,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4489,7 +4056,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -4509,7 +4076,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4518,7 +4085,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0" }, "funding": [ { @@ -4534,45 +4101,38 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2024-06-19T12:35:24+00:00" }, { "name": "symfony/routing", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8f068b792e515b25e26855ac8dc7fe800399f3e5" + "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8f068b792e515b25e26855ac8dc7fe800399f3e5", - "reference": "8f068b792e515b25e26855ac8dc7fe800399f3e5", + "url": "https://api.github.com/repos/symfony/routing/zipball/8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", + "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/yaml": "<5.4" + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" }, "require-dev": { - "doctrine/annotations": "^1.12", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "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" + "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": { @@ -4606,7 +4166,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.1.1" + "source": "https://github.com/symfony/routing/tree/v7.1.3" }, "funding": [ { @@ -4622,37 +4182,34 @@ "type": "tidelift" } ], - "time": "2022-06-08T12:21:15+00:00" + "time": "2024-07-17T06:10:24+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4662,7 +4219,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4689,7 +4249,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -4705,37 +4265,39 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d3edc75baf9f1d4f94879764dda2e1ac33499529" + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d3edc75baf9f1d4f94879764dda2e1ac33499529", - "reference": "d3edc75baf9f1d4f94879764dda2e1ac33499529", + "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "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": { @@ -4774,7 +4336,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.1.0" + "source": "https://github.com/symfony/string/tree/v7.1.3" }, "funding": [ { @@ -4790,32 +4352,29 @@ "type": "tidelift" } ], - "time": "2022-04-22T08:18:23+00:00" + "time": "2024-07-22T10:25:37+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.1.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "bfddd2a1faa271b782b791c361cc16e2dd49dfaa" + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/bfddd2a1faa271b782b791c361cc16e2dd49dfaa", - "reference": "bfddd2a1faa271b782b791c361cc16e2dd49dfaa", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", "shasum": "" }, "require": { "php": ">=8.1" }, - "suggest": { - "symfony/translation-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4855,7 +4414,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" }, "funding": [ { @@ -4871,86 +4430,73 @@ "type": "tidelift" } ], - "time": "2022-04-22T07:30:54+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/twig-bridge", - "version": "v6.1.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "53ce2d7811500c0f0f94af700307ff5b1e305d3c" + "reference": "96e6e12a63db80bcedefc012042d2cb2d1a015f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/53ce2d7811500c0f0f94af700307ff5b1e305d3c", - "reference": "53ce2d7811500c0f0f94af700307ff5b1e305d3c", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/96e6e12a63db80bcedefc012042d2cb2d1a015f8", + "reference": "96e6e12a63db80bcedefc012042d2cb2d1a015f8", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/translation-contracts": "^1.1|^2|^3", - "twig/twig": "^2.13|^3.0.4" + "php": ">=8.2", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^3.9" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<5.4", - "symfony/form": "<6.1", - "symfony/http-foundation": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/translation": "<5.4", - "symfony/workflow": "<5.4" + "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" }, "require-dev": { - "doctrine/annotations": "^1.12", - "egulias/email-validator": "^2.1.10|^3", + "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": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/form": "^6.1", - "symfony/html-sanitizer": "^6.1", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.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": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^5.4|^6.0", - "symfony/security-csrf": "^5.4|^6.0", - "symfony/security-http": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/web-link": "^5.4|^6.0", - "symfony/workflow": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.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" }, - "suggest": { - "symfony/asset": "For using the AssetExtension", - "symfony/expression-language": "For using the ExpressionExtension", - "symfony/finder": "", - "symfony/form": "For using the FormExtension", - "symfony/html-sanitizer": "For using the HtmlSanitizerExtension", - "symfony/http-kernel": "For using the HttpKernelExtension", - "symfony/routing": "For using the RoutingExtension", - "symfony/security-core": "For using the SecurityExtension", - "symfony/security-csrf": "For using the CsrfExtension", - "symfony/security-http": "For using the LogoutUrlExtension", - "symfony/stopwatch": "For using the StopwatchExtension", - "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", "autoload": { "psr-4": { @@ -4977,7 +4523,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v6.1.0" + "source": "https://github.com/symfony/twig-bridge/tree/v7.1.1" }, "funding": [ { @@ -4993,49 +4539,47 @@ "type": "tidelift" } ], - "time": "2022-05-21T13:34:40+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/twig-bundle", - "version": "v6.1.1", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "a2abab10068525a7f5a879e40e411d369d688545" + "reference": "d48c2f08c2f315e749f0e18fc4945b7be8afe1e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/a2abab10068525a7f5a879e40e411d369d688545", - "reference": "a2abab10068525a7f5a879e40e411d369d688545", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/d48c2f08c2f315e749f0e18fc4945b7be8afe1e5", + "reference": "d48c2f08c2f315e749f0e18fc4945b7be8afe1e5", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.1", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/twig-bridge": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" + "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" }, "conflict": { - "symfony/framework-bundle": "<5.4", - "symfony/translation": "<5.4" + "symfony/framework-bundle": "<6.4", + "symfony/translation": "<6.4" }, "require-dev": { - "doctrine/annotations": "^1.10.4", - "symfony/asset": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/form": "^5.4|^6.0", - "symfony/framework-bundle": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/web-link": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "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": { @@ -5063,7 +4607,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v6.1.1" + "source": "https://github.com/symfony/twig-bundle/tree/v7.1.1" }, "funding": [ { @@ -5079,71 +4623,59 @@ "type": "tidelift" } ], - "time": "2022-05-27T16:55:36+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/validator", - "version": "v6.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "b2ae30b952165080e810c3a118b230184cb97db0" + "reference": "ba711a6cfc008544dad059abb3c1d997f1472237" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/b2ae30b952165080e810c3a118b230184cb97db0", - "reference": "b2ae30b952165080e810c3a118b230184cb97db0", + "url": "https://api.github.com/repos/symfony/validator/zipball/ba711a6cfc008544dad059abb3c1d997f1472237", + "reference": "ba711a6cfc008544dad059abb3c1d997f1472237", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^1.1|^2|^3" + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" }, "conflict": { - "doctrine/annotations": "<1.13", "doctrine/lexer": "<1.1", - "phpunit/phpunit": "<5.4.3", - "symfony/dependency-injection": "<5.4", - "symfony/expression-language": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/intl": "<5.4", - "symfony/property-info": "<5.4", - "symfony/translation": "<5.4", - "symfony/yaml": "<5.4" + "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": { - "doctrine/annotations": "^1.13", - "egulias/email-validator": "^2.1.10|^3", - "symfony/cache": "^5.4|^6.0", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "egulias/email-validator": "Strict (RFC compliant) email validation", - "psr/cache-implementation": "For using the mapping cache.", - "symfony/config": "", - "symfony/expression-language": "For using the Expression validator and the ExpressionLanguageSyntax constraints", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For accessing properties within comparison constraints", - "symfony/property-info": "To automatically add NotNull and Type constraints", - "symfony/translation": "For translating validation errors.", - "symfony/yaml": "" + "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": { @@ -5151,7 +4683,8 @@ "Symfony\\Component\\Validator\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/bin/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5171,7 +4704,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.1.1" + "source": "https://github.com/symfony/validator/tree/v7.1.3" }, "funding": [ { @@ -5187,41 +4720,36 @@ "type": "tidelift" } ], - "time": "2022-06-09T12:51:38+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.1.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "98587d939cb783aa04e828e8fa857edaca24c212" + "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/98587d939cb783aa04e828e8fa857edaca24c212", - "reference": "98587d939cb783aa04e828e8fa857edaca24c212", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/86af4617cca75a6e28598f49ae0690f3b9d4591f", + "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "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" @@ -5259,7 +4787,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.1.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.1.3" }, "funding": [ { @@ -5275,27 +4803,29 @@ "type": "tidelift" } ], - "time": "2022-05-21T13:34:40+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.1.1", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "ce1452317b1210ddfe18d143fa8a09c18f034b89" + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/ce1452317b1210ddfe18d143fa8a09c18f034b89", - "reference": "ce1452317b1210ddfe18d143fa8a09c18f034b89", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5328,10 +4858,12 @@ "export", "hydrate", "instantiate", + "lazy-loading", + "proxy", "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.1.1" + "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" }, "funding": [ { @@ -5347,34 +4879,31 @@ "type": "tidelift" } ], - "time": "2022-05-27T12:58:07+00:00" + "time": "2024-06-28T08:00:31+00:00" }, { "name": "symfony/yaml", - "version": "v6.1.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "84ce4f9d2d68f306f971a39d949d8f4b5550dba2" + "reference": "fa34c77015aa6720469db7003567b9f772492bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/84ce4f9d2d68f306f971a39d949d8f4b5550dba2", - "reference": "84ce4f9d2d68f306f971a39d949d8f4b5550dba2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^6.4|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -5405,7 +4934,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.1.0" + "source": "https://github.com/symfony/yaml/tree/v7.1.1" }, "funding": [ { @@ -5421,38 +4950,41 @@ "type": "tidelift" } ], - "time": "2022-04-15T14:25:02+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "twig/twig", - "version": "v3.4.1", + "version": "v3.10.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342" + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e939eae92386b69b49cfa4599dd9bead6bf4a342", - "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", "shasum": "" }, "require": { "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/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -5485,7 +5017,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.4.1" + "source": "https://github.com/twigphp/Twig/tree/v3.10.3" }, "funding": [ { @@ -5497,22 +5029,81 @@ "type": "tidelift" } ], - "time": "2022-05-17T05:48:52+00:00" + "time": "2024-05-16T10:04:27+00:00" + }, + { + "name": "webimpress/safe-writer", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/webimpress/safe-writer.git", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.4", + "vimeo/psalm": "^4.7", + "webimpress/coding-standard": "^1.2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev", + "dev-develop": "2.3.x-dev", + "dev-release-1.0": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Webimpress\\SafeWriter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "Tool to write files safely, to avoid race conditions", + "keywords": [ + "concurrent write", + "file writer", + "race condition", + "safe writer", + "webimpress" + ], + "support": { + "issues": "https://github.com/webimpress/safe-writer/issues", + "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2021-04-19T16:34:45+00:00" } ], "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.2", + "version": "v2.6.4", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", "shasum": "" }, "require": { @@ -5524,8 +5115,8 @@ "ext-json": "*", "jetbrains/phpstorm-stubs": "^2019.3", "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", "extra": { @@ -5580,7 +5171,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" + "source": "https://github.com/amphp/amp/tree/v2.6.4" }, "funding": [ { @@ -5588,20 +5179,20 @@ "type": "github" } ], - "time": "2022-02-20T17:52:18+00:00" + "time": "2024-03-21T18:52:26+00:00" }, { "name": "amphp/byte-stream", - "version": "v1.8.1", + "version": "v1.8.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, "require": { @@ -5617,11 +5208,6 @@ "psalm/phar": "^3.11.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "files": [ "lib/functions.php" @@ -5645,7 +5231,7 @@ } ], "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", + "homepage": "https://amphp.org/byte-stream", "keywords": [ "amp", "amphp", @@ -5655,9 +5241,8 @@ "stream" ], "support": { - "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" }, "funding": [ { @@ -5665,20 +5250,20 @@ "type": "github" } ], - "time": "2021-03-30T17:13:30+00:00" + "time": "2024-04-13T18:00:56+00:00" }, { "name": "behat/behat", - "version": "v3.10.0", + "version": "v3.14.0", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "a55661154079cf881ef643b303bfaf67bae3a09f" + "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/a55661154079cf881ef643b303bfaf67bae3a09f", - "reference": "a55661154079cf881ef643b303bfaf67bae3a09f", + "url": "https://api.github.com/repos/Behat/Behat/zipball/2a3832d9cb853a794af3a576f9e524ae460f3340", + "reference": "2a3832d9cb853a794af3a576f9e524ae460f3340", "shasum": "" }, "require": { @@ -5686,19 +5271,19 @@ "behat/transliterator": "^1.2", "ext-mbstring": "*", "php": "^7.2 || ^8.0", - "psr/container": "^1.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/console": "^4.4 || ^5.0 || ^6.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", - "symfony/translation": "^4.4 || ^5.0 || ^6.0", - "symfony/yaml": "^4.4 || ^5.0 || ^6.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": { - "container-interop/container-interop": "^1.2", "herrera-io/box": "~1.6.1", + "phpspec/prophecy": "^1.15", "phpunit/phpunit": "^8.5 || ^9.0", - "symfony/process": "^4.4 || ^5.0 || ^6.0", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0", "vimeo/psalm": "^4.8" }, "suggest": { @@ -5750,9 +5335,9 @@ ], "support": { "issues": "https://github.com/Behat/Behat/issues", - "source": "https://github.com/Behat/Behat/tree/v3.10.0" + "source": "https://github.com/Behat/Behat/tree/v3.14.0" }, - "time": "2021-11-02T20:09:40+00:00" + "time": "2023-12-09T13:55:02+00:00" }, { "name": "behat/gherkin", @@ -5819,26 +5404,28 @@ }, { "name": "behat/mink", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/minkphp/Mink.git", - "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5" + "reference": "d8527fdf8785aad38455fb426af457ab9937aece" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/Mink/zipball/19e58905632e7cfdc5b2bafb9b950a3521af32c5", - "reference": "19e58905632e7cfdc5b2bafb9b950a3521af32c5", + "url": "https://api.github.com/repos/minkphp/Mink/zipball/d8527fdf8785aad38455fb426af457ab9937aece", + "reference": "d8527fdf8785aad38455fb426af457ab9937aece", "shasum": "" }, "require": { "php": ">=7.2", - "symfony/css-selector": "^4.4 || ^5.0 || ^6.0" + "symfony/css-selector": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { + "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", - "symfony/phpunit-bridge": "^5.4 || ^6.0" + "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": "fast headless driver for any app without JS emulation", @@ -5877,37 +5464,40 @@ ], "support": { "issues": "https://github.com/minkphp/Mink/issues", - "source": "https://github.com/minkphp/Mink/tree/v1.10.0" + "source": "https://github.com/minkphp/Mink/tree/v1.11.0" }, - "time": "2022-03-28T14:22:43+00:00" + "time": "2023-12-09T11:23:23+00:00" }, { "name": "behat/mink-browserkit-driver", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/minkphp/MinkBrowserKitDriver.git", - "reference": "d2768e6c17b293d86d8fcff54cbb9e6ad938fee1" + "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/d2768e6c17b293d86d8fcff54cbb9e6ad938fee1", - "reference": "d2768e6c17b293d86d8fcff54cbb9e6ad938fee1", + "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/16d53476e42827ed3aafbfa4fde17a1743eafd50", + "reference": "16d53476e42827ed3aafbfa4fde17a1743eafd50", "shasum": "" }, "require": { - "behat/mink": "^1.9.0@dev", + "behat/mink": "^1.11.0@dev", + "ext-dom": "*", "php": ">=7.2", - "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0", - "symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0" + "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", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/error-handler": "^4.4 || ^5.0 || ^6.0", - "symfony/http-client": "^4.4 || ^5.0 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", - "symfony/mime": "^4.4 || ^5.0 || ^6.0", + "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", @@ -5942,9 +5532,9 @@ ], "support": { "issues": "https://github.com/minkphp/MinkBrowserKitDriver/issues", - "source": "https://github.com/minkphp/MinkBrowserKitDriver/tree/v2.1.0" + "source": "https://github.com/minkphp/MinkBrowserKitDriver/tree/v2.2.0" }, - "time": "2022-03-28T14:33:51+00:00" + "time": "2023-12-09T11:30:50+00:00" }, { "name": "behat/transliterator", @@ -5995,6 +5585,56 @@ }, "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", @@ -6070,30 +5710,38 @@ }, { "name": "composer/pcre", - "version": "3.0.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "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.3", + "phpstan/phpstan": "^1.11.8", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -6121,7 +5769,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.0.0" + "source": "https://github.com/composer/pcre/tree/3.2.0" }, "funding": [ { @@ -6137,20 +5785,20 @@ "type": "tidelift" } ], - "time": "2022-02-25T20:21:48+00:00" + "time": "2024-07-25T09:36:02+00:00" }, { "name": "composer/semver", - "version": "3.3.2", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", "shasum": "" }, "require": { @@ -6200,9 +5848,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "source": "https://github.com/composer/semver/tree/3.4.2" }, "funding": [ { @@ -6218,20 +5866,20 @@ "type": "tidelift" } ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2024-07-12T11:35:52+00:00" }, { "name": "composer/xdebug-handler", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { @@ -6242,7 +5890,7 @@ "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -6266,9 +5914,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "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.3" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { @@ -6284,7 +5932,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -6307,110 +5955,38 @@ "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" }, "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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": "doctrine/annotations", - "version": "1.13.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], + "description": "implementation of xdg base directory specification for php", "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.2" + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" }, - "time": "2021-08-05T19:00:23+00:00" + "time": "2019-12-04T15:06:13+00:00" }, { "name": "fakerphp/faker", - "version": "v1.19.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75" + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/d7f08a622b3346766325488aa32ddc93ccdecc75", - "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", + "php": "^7.4 || ^8.0", "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" }, @@ -6421,7 +5997,8 @@ "bamarni/composer-bin-plugin": "^1.4.1", "doctrine/persistence": "^1.3 || ^2.0", "ext-intl": "*", - "symfony/phpunit-bridge": "^4.4 || ^5.2" + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" }, "suggest": { "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", @@ -6431,11 +6008,6 @@ "ext-mbstring": "Required for multibyte Unicode string functionality." }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "v1.19-dev" - } - }, "autoload": { "psr-4": { "Faker\\": "src/Faker/" @@ -6458,9 +6030,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.19.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" }, - "time": "2022-02-02T17:38:57+00:00" + "time": "2024-01-02T13:46:09+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -6563,31 +6135,92 @@ }, "time": "2022-03-02T22:36:06+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "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", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "ThΓ©o FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-02-07T09:43:46+00:00" + }, { "name": "friends-of-behat/mink-extension", - "version": "v2.6.1", + "version": "v2.7.5", "source": { "type": "git", "url": "https://github.com/FriendsOfBehat/MinkExtension.git", - "reference": "df04efb3e88833208c3a99a3efa3f7e9f03854db" + "reference": "854336030e11983f580f49faad1b49a1238f9846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/df04efb3e88833208c3a99a3efa3f7e9f03854db", - "reference": "df04efb3e88833208c3a99a3efa3f7e9f03854db", + "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/854336030e11983f580f49faad1b49a1238f9846", + "reference": "854336030e11983f580f49faad1b49a1238f9846", "shasum": "" }, "require": { "behat/behat": "^3.0.5", "behat/mink": "^1.5", "php": ">=7.4", - "symfony/config": "^4.4 || ^5.0 || ^6.0" + "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "replace": { "behat/mink-extension": "self.version" }, "require-dev": { - "behat/mink-goutte-driver": "^1.1", + "behat/mink-goutte-driver": "^1.1 || ^2.0", "phpspec/phpspec": "^6.0 || ^7.0 || 7.1.x-dev" }, "type": "behat-extension", @@ -6624,48 +6257,48 @@ "web" ], "support": { - "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.6.1" + "issues": "https://github.com/FriendsOfBehat/MinkExtension/issues", + "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.5" }, - "time": "2021-12-24T13:19:26+00:00" + "time": "2024-01-11T09:12:02+00:00" }, { "name": "friends-of-behat/symfony-extension", - "version": "v2.3.1", + "version": "v2.6.0", "source": { "type": "git", "url": "https://github.com/FriendsOfBehat/SymfonyExtension.git", - "reference": "572c34e6483e70db0afd45111553332f95bc06ca" + "reference": "dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfBehat/SymfonyExtension/zipball/572c34e6483e70db0afd45111553332f95bc06ca", - "reference": "572c34e6483e70db0afd45111553332f95bc06ca", + "url": "https://api.github.com/repos/FriendsOfBehat/SymfonyExtension/zipball/dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd", + "reference": "dfb1c9c96cc0fb7c8e1caa060695426a12e1efbd", "shasum": "" }, "require": { "behat/behat": "^3.6.1", - "php": "^7.4 || ^8.0", - "symfony/dependency-injection": "^4.4 || ^5.3 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.3 || ^6.0", - "symfony/proxy-manager-bridge": "^4.4 || ^5.3 || ^6.0" + "php": "^8.1", + "symfony/dependency-injection": "^6.2 || ^7.0", + "symfony/http-kernel": "^6.2 || ^7.0" }, "require-dev": { + "behat/mink": "^1.9", + "behat/mink-browserkit-driver": "^2.0", "behat/mink-selenium2-driver": "^1.3", - "friends-of-behat/mink": "^1.9", - "friends-of-behat/mink-browserkit-driver": "^1.5", "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", - "symfony/browser-kit": "^4.4 || ^5.3 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.3 || ^6.0", - "symfony/process": "^4.4 || ^5.3 || ^6.0", - "symfony/yaml": "^4.4 || ^5.3 || ^6.0", - "vimeo/psalm": "4.15.0" + "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" }, "suggest": { - "friends-of-behat/mink": "^1.9", - "friends-of-behat/mink-browserkit-driver": "^1.5", + "behat/mink": "^1.9", + "behat/mink-browserkit-driver": "^2.0", "friends-of-behat/mink-extension": "^2.5" }, "type": "symfony-bundle", @@ -6693,98 +6326,9 @@ "description": "Integrates Behat with Symfony.", "support": { "issues": "https://github.com/FriendsOfBehat/SymfonyExtension/issues", - "source": "https://github.com/FriendsOfBehat/SymfonyExtension/tree/v2.3.1" - }, - "time": "2021-12-24T13:14:59+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", - "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", - "shasum": "" + "source": "https://github.com/FriendsOfBehat/SymfonyExtension/tree/v2.6.0" }, - "require": { - "composer/semver": "^3.2", - "composer/xdebug-handler": "^3.0.3", - "doctrine/annotations": "^1.13", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0", - "php-cs-fixer/diff": "^2.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php80": "^1.25", - "symfony/polyfill-php81": "^1.25", - "symfony/process": "^5.4 || ^6.0", - "symfony/stopwatch": "^5.4 || ^6.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^1.5", - "mikey179/vfsstream": "^1.6.10", - "php-coveralls/php-coveralls": "^2.5.2", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.15", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "phpunitgoodpractices/polyfill": "^1.5", - "phpunitgoodpractices/traits": "^1.9.1", - "symfony/phpunit-bridge": "^6.0", - "symfony/yaml": "^5.4 || ^6.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz RumiΕ„ski", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "support": { - "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2022-03-18T17:20:59+00:00" + "time": "2024-07-03T15:49:43+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -6839,26 +6383,24 @@ }, { "name": "masterminds/html5", - "version": "2.7.5", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f640ac1bdddff06ea333a920c95bbad8872429ab" + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f640ac1bdddff06ea333a920c95bbad8872429ab", - "reference": "f640ac1bdddff06ea333a920c95bbad8872429ab", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-dom": "*", - "ext-libxml": "*", "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { @@ -6902,44 +6444,44 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.7.5" + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" }, - "time": "2021-07-01T14:25:37+00:00" + "time": "2024-03-31T07:05:07+00:00" }, { "name": "mockery/mockery", - "version": "1.5.0", + "version": "1.6.12", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac" + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.3 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, "autoload": { - "psr-0": { - "Mockery": "library/" + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" } }, "notification-url": "https://packagist.org/downloads/", @@ -6950,12 +6492,20 @@ { "name": "PΓ‘draic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -6973,23 +6523,26 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.5.0" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2022-01-20T13:18:17+00:00" + "time": "2024-05-16T03:13:13+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -6997,11 +6550,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -7027,7 +6581,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -7035,20 +6589,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.0.0", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", "shasum": "" }, "require": { @@ -7059,7 +6613,7 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", "squizlabs/php_codesniffer": "~3.5" }, "type": "library", @@ -7084,27 +6638,27 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" }, - "time": "2020-12-01T19:48:11+00:00" + "time": "2024-01-31T06:18:54+00:00" }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.19.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.1" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", @@ -7140,79 +6694,90 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" }, - "time": "2022-05-31T20:59:12+00:00" + "time": "2024-03-17T08:10:35+00:00" }, { - "name": "openlss/lib-array2xml", - "version": "1.0.0", + "name": "pdepend/pdepend", + "version": "2.16.2", "source": { "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + "url": "https://github.com/pdepend/pdepend.git", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", "shasum": "" }, "require": { - "php": ">=5.3.2" + "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": { + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "squizlabs/php_codesniffer": "^2.0.0" }, + "bin": [ + "src/bin/pdepend" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, "autoload": { - "psr-0": { - "LSS": "" + "psr-4": { + "PDepend\\": "src/main/php/PDepend" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } + "BSD-3-Clause" ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", + "description": "Official version of pdepend to be handled with Composer", "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" ], "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" }, - "time": "2019-03-29T20:06:56+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" + } + ], + "time": "2023-12-17T18:09:59+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -7253,9 +6818,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -7309,56 +6880,61 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "php-cs-fixer/diff", - "version": "v2.0.2", + "name": "phpat/phpat", + "version": "0.10.18", "source": { "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + "url": "https://github.com/carlosas/phpat.git", + "reference": "4c29e330fb306876bca3174aa4b097d0d8611964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "url": "https://api.github.com/repos/carlosas/phpat/zipball/4c29e330fb306876bca3174aa4b097d0d8611964", + "reference": "4c29e330fb306876bca3174aa4b097d0d8611964", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0 || ^8.0" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "symfony/process": "^3.3" + "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" + ] + } }, - "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "helpers.php" + ], + "psr-4": { + "PHPat\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "Carlos Alandete Sastre", + "email": "carlos.alandete@gmail.com" } ], - "description": "sebastian/diff v3 backport support for PHP 5.6+", - "homepage": "https://github.com/PHP-CS-Fixer", - "keywords": [ - "diff" - ], + "description": "PHP Architecture Tester", "support": { - "issues": "https://github.com/PHP-CS-Fixer/diff/issues", - "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + "issues": "https://github.com/carlosas/phpat/issues", + "source": "https://github.com/carlosas/phpat/tree/0.10.18" }, - "time": "2020-10-14T08:32:19+00:00" + "time": "2024-07-05T14:56:19+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -7415,28 +6991,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "version": "5.4.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "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": { @@ -7460,37 +7043,45 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "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.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2024-05-21T05:55:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "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": { @@ -7513,99 +7104,220 @@ "email": "me@mikevanriel.com" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "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": "phpmd/phpmd", + "version": "2.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", + "shasum": "" + }, + "require": { + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", + "ext-xml": "*", + "pdepend/pdepend": "^2.16.1", + "php": ">=5.3.9" + }, + "require-dev": { + "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", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "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": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + } + ], + "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": [ + "dev", + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2023-12-11T08:22:20+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.29.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "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" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2022-03-15T21:29:03+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.15.0", + "name": "phpstan/phpstan", + "version": "1.11.9", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "e370bcddadaede0c1716338b262346f40d296f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", + "reference": "e370bcddadaede0c1716338b262346f40d296f82", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "php": "^7.2|^8.0" }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" + "conflict": { + "phpstan/phpstan-shim": "*" }, + "bin": [ + "phpstan", + "phpstan.phar" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "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": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "url": "https://github.com/ondrejmirtes", + "type": "github" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "url": "https://github.com/phpstan", + "type": "github" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" - }, - "time": "2021-12-08T12:19:24+00:00" + "time": "2024-08-01T16:25:18+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -7620,8 +7332,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "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": { @@ -7654,7 +7366,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" }, "funding": [ { @@ -7662,7 +7375,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", @@ -7779,234 +7492,472 @@ "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, - "funding": [ + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "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": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "49d7820565836236411f5dc002d16dd689cde42f" + }, + "dist": { + "type": "zip", + "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": "*", + "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-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" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/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": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "name": "Matt Brown", + "email": "github@muglug.com" } ], - "time": "2020-09-28T05:58:55+00:00" + "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/php-text-template", - "version": "2.0.4", + "name": "psalm/plugin-phpunit", + "version": "0.18.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", "shasum": "" }, "require": { - "php": ">=7.3" + "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": "<7.5" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "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": "library", + "type": "psalm-plugin", "extra": { - "branch-alias": { - "dev-master": "2.0-dev" + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Matt Brown", + "email": "github@muglug.com" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Psalm plugin for PHPUnit", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2022-12-03T07:47:07+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "psalm/plugin-symfony", + "version": "v5.2.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/psalm/psalm-plugin-symfony.git", + "reference": "fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196", + "reference": "fb801a9b3d12ace9fb619febfaa3ae0bc1dbb196", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-simplexml": "*", + "php": "^8.1", + "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "^5.16" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "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" }, - "type": "library", + "suggest": { + "weirdan/doctrine-psalm-plugin": "If Doctrine is used, it is recommended install this plugin" + }, + "type": "psalm-plugin", "extra": { - "branch-alias": { - "dev-master": "5.0-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": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], + "description": "Psalm Plugin for Symfony", "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", + "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.2.5" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2024-07-03T11:57:02+00:00" }, { - "name": "phpunit/phpunit", - "version": "9.5.20", + "name": "rector/rector", + "version": "0.18.13", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + "url": "https://github.com/rectorphp/rector.git", + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/f8011a76d36aa4f839f60f3b4f97707d97176618", + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" + "php": "^7.2|^8.0", + "phpstan/phpstan": "^1.10.35" }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" }, "bin": [ - "phpunit" + "bin/rector" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, "autoload": { "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } + "MIT" ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Instant Upgrade and Automated Refactoring of any PHP code", "keywords": [ - "phpunit", - "testing", - "xunit" + "automation", + "dev", + "migration", + "refactoring" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/0.18.13" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/tomasvotruba", "type": "github" } ], - "time": "2022-04-01T12:37:26+00:00" + "time": "2023-12-20T16:08:01+00:00" }, { "name": "roave/security-advisories", @@ -8014,359 +7965,607 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "bf362097fbfe4580b99b696dd8208b24ffd112b2" + "reference": "ff7456939acba6dd515a8a10aad66be6bc1b8dc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bf362097fbfe4580b99b696dd8208b24ffd112b2", - "reference": "bf362097fbfe4580b99b696dd8208b24ffd112b2", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ff7456939acba6dd515a8a10aad66be6bc1b8dc1", + "reference": "ff7456939acba6dd515a8a10aad66be6bc1b8dc1", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "admidio/admidio": "<4.1.9", + "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", - "alextselegidis/easyappointments": "<=1.4.3", + "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", + "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", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", - "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", + "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", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bagisto/bagisto": "<0.1.5", + "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", - "baserproject/basercms": "<4.5.4", - "billz/raspap-webgui": "<=2.6.6", + "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", "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": "<3.10.3|>=4,<4.0.6", + "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", "catfan/medoo": "<1.7.5", - "centreon/centreon": "<20.10.7", + "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.0.6", - "codeigniter4/framework": "<4.1.9", + "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.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5", - "concrete5/concrete5": "<9", - "concrete5/core": "<8.5.7", + "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/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3", - "contao/core": ">=2,<3.5.39", - "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3|= 4.10.0", - "contao/listing-bundle": ">=4,<4.4.8", + "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", - "craftcms/cms": "<3.7.36", - "croogo/croogo": "<3.0.7", - "cuyz/valinor": ">=0.5,<0.7", + "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", - "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", - "directmailteam/direct-mail": "<5.2.4", - "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/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|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "= 12.0.5|<16|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": "<1.2.1", - "drupal/core": ">=7,<7.88|>=8,<9.2.13|>=9.3,<9.3.6", - "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "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", "ether/logs": "<3.0.4", - "ezsystems/demobundle": ">=5.4,<5.4.6.1", + "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", - "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", + "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.27", + "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-kernel": "<=1.2.5|>=1.3,<1.3.19", + "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", + "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.1|>=7,<7.5.29", - "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.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", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", "ezyang/htmlpurifier": "<4.1.1", "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "facturascripts/facturascripts": "<=2022.8", + "facturascripts/facturascripts": "<=2022.08", + "fastly/magento2": "<1.2.26", "feehi/cms": "<=2.1.1", - "feehi/feehicms": "<=0.1.3", + "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", "filegator/filegator": "<7.8", - "firebase/php-jwt": "<2", - "flarum/core": ">=1,<=1.0.1", - "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", - "flarum/tags": "<=0.1-beta.13", + "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": "<9", + "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", - "froala/wysiwyg-editor": "<3.2.7", - "froxlor/froxlor": "<=0.10.22", + "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", + "funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", - "getgrav/grav": "<1.7.33", - "getkirby/cms": "<3.5.8", + "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", - "gilacms/gila": "<=1.11.4", + "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", + "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.6.5", - "guzzlehttp/guzzle": "<6.5.7|>=7,<7.4.4", - "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", - "helloxz/imgurl": "= 2.31|<=2.31", + "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/core": ">=4,<4.0.7|>=4.1,<4.1.4", + "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", - "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.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", + "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", - "impresscms/impresscms": "<=1.4.3", - "in2code/femanager": "<5.5.1|>=6,<6.3.1", - "intelliants/subrion": "<=4.2.1", + "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", "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", - "kitodo/presentation": "<3.1.2", + "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", + "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.42|>=7,<7.30.6|>=8,<8.75", - "laravel/laravel": "<=9.1.8", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "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": "<=5.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", "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", - "librenms/librenms": "<22.4", + "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", + "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,<2.2.10|>=2.3,<2.3.3", - "magento/magento1ce": "<1.9.4.3", - "magento/magento1ee": ">=1,<1.14.4.3", - "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", + "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.3|= 2.13.1", - "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", - "microweber/microweber": "<1.3", + "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", - "modx/revolution": "<= 2.8.3-pl|<2.8", + "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.0.1", + "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.4", + "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/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=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": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "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": "<=2.1.27.36", + "nilsteampassnet/teampass": "<3.0.10", + "nonfiction/nterchange": "<4.1.1", + "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", - "nukeviet/nukeviet": "<4.3.4", + "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.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", - "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", + "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.475|>=1.1,<1.1.11|>=2,<2.1.27", + "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.9.3|>=2,<2.1.5", + "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", "open-web-analytics/open-web-analytics": "<1.7.4", - "opencart/opencart": "<=3.0.3.2", + "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<19.4.15|>=20,<20.0.13", - "orchid/platform": ">=9,<9.4.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/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<4.2.8", + "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": "<2.11", + "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", + "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.1.3", + "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.31|>=3,<3.0.7", - "phpservermon/phpservermon": "<=3.5.2", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5,<5.6.3", + "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", + "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/pimcore": "<10.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": ">= 4.0.0-BETA5, < 4.4.2|<4.2.10", + "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/contactform": ">1.0.1,<4.3", + "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": ">=1.7,<=1.7.8.2", - "prestashop/productcomments": ">=4,<4.2.1", + "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", - "propel/propel": ">=2-alpha.1,<=2-alpha.7", + "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.7", + "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", - "pwweb/laravel-core": "<=0.3.6-beta", + "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", - "remdex/livehelperchat": "<3.99", + "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": "<3.0.4", + "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.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", + "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/core": "<=6.4.9", - "shopware/platform": "<=6.4.9", + "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.9", - "shopware/storefront": "<=6.4.8.1", - "shopxo/shopxo": "<2.2.6", + "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/admin": ">=1,<1.8.1", - "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", - "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", - "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", + "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": "<4.10.1", - "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1", + "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/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", + "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.1.1", + "silverstripe/subsites": ">=2,<2.6.1", "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", - "silverstripe/userforms": "<3", + "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/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.45|>=4,<4.1.1", - "snipe/snipe-it": "<5.4.4|>= 6.0.0-RC-1, <= 6.0.0-RC-5", + "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", - "spipu/html2pdf": "<5.2.4", + "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", - "ssddanbrown/bookstack": "<22.2.3", - "statamic/cms": "<3.2.39|>=3.3,<3.3.2", - "stormpath/sdk": ">=0,<9.9.99", - "studio-42/elfinder": "<2.1.59", - "subrion/cms": "<=4.2.1", - "sulu/sulu": "= 2.4.0-RC1|<1.6.44|>=2,<2.2.18|>=2.3,<2.3.8", - "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/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.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2", - "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", + "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,<4", + "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|>=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.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3|= 6.0.3|= 5.4.3|= 5.3.14", + "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,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5|>=5.2,<5.3.12", + "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", @@ -8376,79 +8575,135 @@ "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.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", - "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|>=5.3,<5.3.12", + "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,<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", + "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,<3.4.49|>=4,<4.4.35|>=5,<5.3.12|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3", + "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", - "t3/dce": ">=2.2,<2.6.2", + "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", - "tecnickcom/tcpdf": "<6.2.22", + "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-beta.1,<2.1.3", + "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", - "tinymce/tinymce": "<5.10", - "titon/framework": ">=0,<9.9.99", - "topthink/framework": "<6.0.12", - "topthink/think": "<=6.0.9", + "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", - "tribalsystems/zenario": "<9.2.55826", + "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.14.11|>=3,<3.3.8", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", - "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.52|>=8,<=8.7.41|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", + "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", - "unisharp/laravel-filemanager": "<=2.3", + "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", - "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", + "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", + "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", - "wp-cli/wp-cli": "<2.5", - "wp-graphql/wp-graphql": "<0.3.5", + "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", - "wwbn/avideo": "<=11.6", + "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", + "yetiforce/yetiforce-crm": "<=6.4", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.38", + "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.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.8.4", "zendframework/zend-feed": "<2.10.3", @@ -8456,21 +8711,30 @@ "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": "<=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", - "zoujingli/thinkadmin": "<6.0.22" + "zoujingli/thinkadmin": "<=6.1.53" }, "type": "metapackage", "notification-url": "https://packagist.org/downloads/", @@ -8490,6 +8754,9 @@ } ], "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" @@ -8504,20 +8771,20 @@ "type": "tidelift" } ], - "time": "2022-06-10T00:15:15+00:00" + "time": "2024-08-05T15:04:41+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -8552,7 +8819,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -8560,7 +8827,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -8675,16 +8942,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -8737,7 +9004,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -8745,24 +9012,24 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -8794,7 +9061,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -8802,20 +9069,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -8860,7 +9127,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -8868,20 +9135,20 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -8923,7 +9190,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -8931,20 +9198,20 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -9000,7 +9267,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -9008,20 +9275,20 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -9064,7 +9331,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -9072,24 +9339,24 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -9121,7 +9388,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -9129,7 +9396,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", @@ -9245,16 +9512,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -9293,10 +9560,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "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.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -9304,20 +9571,20 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -9329,7 +9596,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -9350,8 +9617,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -9359,20 +9625,20 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", - "version": "3.0.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -9384,262 +9650,125 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the 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.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-03-15T09:54:48+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v6.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "b839cef6e6526bd2090efe92c218d8c7479a51e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b839cef6e6526bd2090efe92c218d8c7479a51e0", - "reference": "b839cef6e6526bd2090efe92c218d8c7479a51e0", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/dom-crawler": "^5.4|^6.0" - }, - "require-dev": { - "symfony/css-selector": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", - "homepage": "https://symfony.com", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.1.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-05-06T20:04:05+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { - "name": "symfony/css-selector", - "version": "v6.1.0", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "05c40f02f621609404b8820ff8bc39acb46e19cf" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/05c40f02f621609404b8820ff8bc39acb46e19cf", - "reference": "05c40f02f621609404b8820ff8bc39acb46e19cf", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "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": "Jean-FranΓ§ois Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.1.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "symfony/dom-crawler", - "version": "v6.1.0", + "name": "spatie/array-to-xml", + "version": "3.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "baacc99edd169bd8e06723f4c38150ef97feca0f" + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/baacc99edd169bd8e06723f4c38150ef97feca0f", - "reference": "baacc99edd169bd8e06723f4c38150ef97feca0f", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" + "ext-dom": "*", + "php": "^8.0" }, "require-dev": { - "symfony/css-selector": "^5.4|^6.0" - }, - "suggest": { - "symfony/css-selector": "" + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Spatie\\ArrayToXml\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -9647,57 +9776,62 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" } ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.1.0" + "source": "https://github.com/spatie/array-to-xml/tree/3.3.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://spatie.be/open-source/support-us", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/spatie", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-05-04T14:48:14+00:00" + "time": "2024-05-01T10:20:27+00:00" }, { - "name": "symfony/options-resolver", - "version": "v6.1.0", + "name": "symfony/browser-kit", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" + "url": "https://github.com/symfony/browser-kit.git", + "reference": "9c13742e3175b5815e272b981876ae329bec2040" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", - "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9c13742e3175b5815e272b981876ae329bec2040", + "reference": "9c13742e3175b5815e272b981876ae329bec2040", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.2", + "symfony/dom-crawler": "^6.4|^7.0" + }, + "require-dev": { + "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", "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" + "Symfony\\Component\\BrowserKit\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9717,15 +9851,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an improved replacement for the array_replace PHP function", + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" + "source": "https://github.com/symfony/browser-kit/tree/v7.1.1" }, "funding": [ { @@ -9741,29 +9870,29 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/process", - "version": "v6.1.0", + "name": "symfony/css-selector", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "318718453c2be58266f1a9e74063d13cb8dd4165" + "url": "https://github.com/symfony/css-selector.git", + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/318718453c2be58266f1a9e74063d13cb8dd4165", - "reference": "318718453c2be58266f1a9e74063d13cb8dd4165", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\CssSelector\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9778,15 +9907,19 @@ "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": "Executes commands in sub-processes", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.1.0" + "source": "https://github.com/symfony/css-selector/tree/v7.1.1" }, "funding": [ { @@ -9802,34 +9935,35 @@ "type": "tidelift" } ], - "time": "2022-05-11T12:12:29+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/proxy-manager-bridge", - "version": "v6.1.0", + "name": "symfony/dom-crawler", + "version": "v7.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/proxy-manager-bridge.git", - "reference": "bcb3e7a28ee373bd64ecd2a2289a64c5860716af" + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/proxy-manager-bridge/zipball/bcb3e7a28ee373bd64ecd2a2289a64c5860716af", - "reference": "bcb3e7a28ee373bd64ecd2a2289a64c5860716af", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/01ce8174447f1f1dd33a5854b01beef79061d9fa", + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa", "shasum": "" }, "require": { - "friendsofphp/proxy-manager-lts": "^1.0.2", - "php": ">=8.1", - "symfony/dependency-injection": "^5.4|^6.0" + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/config": "^5.4|^6.0" + "symfony/css-selector": "^6.4|^7.0" }, - "type": "symfony-bridge", + "type": "library", "autoload": { "psr-4": { - "Symfony\\Bridge\\ProxyManager\\": "" + "Symfony\\Component\\DomCrawler\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9849,10 +9983,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides integration for ProxyManager with various Symfony components", + "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/proxy-manager-bridge/tree/v6.1.0" + "source": "https://github.com/symfony/dom-crawler/tree/v7.1.1" }, "funding": [ { @@ -9868,30 +10002,62 @@ "type": "tidelift" } ], - "time": "2022-03-02T13:21:45+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { - "name": "symfony/stopwatch", - "version": "v6.1.0", + "name": "symfony/translation", + "version": "v7.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "77dedae82ce2a26e2e9b481855473fc3b3e4e54d" + "url": "https://github.com/symfony/translation.git", + "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/77dedae82ce2a26e2e9b481855473fc3b3e4e54d", - "reference": "77dedae82ce2a26e2e9b481855473fc3b3e4e54d", + "url": "https://api.github.com/repos/symfony/translation/zipball/8d5e50c813ba2859a6dfc99a0765c550507934a1", + "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" + "Symfony\\Component\\Translation\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9911,10 +10077,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.1.0" + "source": "https://github.com/symfony/translation/tree/v7.1.3" }, "funding": [ { @@ -9930,116 +10096,81 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { - "name": "symfony/translation", - "version": "v6.1.0", + "name": "symplify/easy-coding-standard", + "version": "12.3.4", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "b254416631615bc6fe49b0a67f18658827288147" + "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", + "reference": "03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/b254416631615bc6fe49b0a67f18658827288147", - "reference": "b254416631615bc6fe49b0a67f18658827288147", + "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e", + "reference": "03cd792d7fa6d9dc59b6e12a5ca73d9873ee9c0e", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.3|^3.0" + "php": ">=7.2" }, "conflict": { - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/twig-bundle": "<5.4", - "symfony/yaml": "<5.4" - }, - "provide": { - "symfony/translation-implementation": "2.3|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^5.4|^6.0" + "friendsofphp/php-cs-fixer": "<3.46", + "phpcsstandards/php_codesniffer": "<3.8", + "symplify/coding-standard": "<12.1" }, "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "ext-dom": "Needed to support checkstyle output format in class CheckstyleOutputFormatter" }, + "bin": [ + "bin/ecs" + ], "type": "library", "autoload": { "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", + "keywords": [ + "Code style", + "automation", + "fixer", + "static analysis" ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.1.0" + "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": [ { - "url": "https://symfony.com/sponsor", + "url": "https://www.paypal.me/rectorphp", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/tomasvotruba", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-05-11T12:12:29+00:00" + "time": "2024-08-01T07:55:09+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -10068,7 +10199,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -10076,28 +10207,28 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "vimeo/psalm", - "version": "4.23.0", + "version": "5.25.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88" + "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1fe6ff483bf325c803df9f510d09a03fd796f88", - "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88", + "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/package-versions-deprecated": "^1.8.0", + "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", "ext-ctype": "*", "ext-dom": "*", @@ -10106,34 +10237,38 @@ "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.5", + "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.13", - "openlss/lib-array2xml": "^1.0", - "php": "^7.1|^8", - "sebastian/diff": "^3.0 || ^4.0", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", - "symfony/polyfill-php80": "^1.25", - "webmozart/path-util": "^2.3" + "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": { - "bamarni/composer-bin-plugin": "^1.2", - "brianium/paratest": "^4.0||^6.0", + "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", - "phpdocumentor/reflection-docblock": "^5", - "phpmyadmin/sql-parser": "5.1.0||dev-master", - "phpspec/prophecy": ">=1.9.0", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.16", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^4.3 || ^5.0 || ^6.0", - "weirdan/prophecy-shim": "^1.0 || ^2.0" + "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", @@ -10146,20 +10281,17 @@ "psalm-refactor", "psalter" ], - "type": "library", + "type": "project", "extra": { "branch-alias": { - "dev-master": "4.x-dev", + "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" } }, "autoload": { - "files": [ - "src/functions.php", - "src/spl_object_id.php" - ], "psr-4": { "Psalm\\": "src/Psalm/" } @@ -10177,13 +10309,15 @@ "keywords": [ "code", "inspection", - "php" + "php", + "static analysis" ], "support": { + "docs": "https://psalm.dev/docs", "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.23.0" + "source": "https://github.com/vimeo/psalm" }, - "time": "2022-04-28T17:35:49+00:00" + "time": "2024-06-16T15:08:35+00:00" }, { "name": "webmozart/assert", @@ -10242,57 +10376,6 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webmozart/path-util", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/path-util.git", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "webmozart/assert": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\PathUtil\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", - "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" - }, - "abandoned": "symfony/filesystem", - "time": "2015-12-17T08:42:14+00:00" } ], "aliases": [], @@ -10303,7 +10386,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1", + "php": "^8.3", "ext-amqp": "*", "ext-apcu": "*", "ext-json": "*", @@ -10313,5 +10396,5 @@ "platform-dev": { "ext-xdebug": "*" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/docker-compose.yml b/docker-compose.yml index d720f5b1e..19fc0366b 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,7 +67,7 @@ services: - shared_rabbitmq - shared_prometheus - backoffice_elasticsearch - command: symfony serve --dir=apps/backoffice/backend/public --port=8040 --force-php-discovery + command: symfony serve --dir=apps/backoffice/backend/public --port=8040 backoffice_frontend_php: container_name: codely-php_ddd_skeleton-backoffice_frontend-php @@ -86,7 +86,7 @@ services: - shared_prometheus - backoffice_elasticsearch - mooc_mysql - command: symfony serve --dir=apps/backoffice/frontend/public --port=8041 --force-php-discovery + command: symfony serve --dir=apps/backoffice/frontend/public --port=8041 mooc_backend_php: container_name: codely-php_ddd_skeleton-mooc_backend-php @@ -104,4 +104,4 @@ services: - shared_rabbitmq - shared_prometheus - mooc_mysql - command: symfony serve --dir=apps/mooc/backend/public --port=8030 --force-php-discovery + 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/databases/mooc.sql b/etc/databases/mooc.sql index e236cbc46..1a094f827 100644 --- a/etc/databases/mooc.sql +++ b/etc/databases/mooc.sql @@ -1,31 +1,129 @@ -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 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` VALUES ("cdf26d7d-3deb-4e8c-9f73-4ac085a8d6f3", 0, "[]"); - -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; - -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; +/* ------------------------- + 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/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/extensions/rabbitmq.sh b/etc/infrastructure/php/extensions/rabbitmq.sh deleted file mode 100644 index 41fc6072f..000000000 --- a/etc/infrastructure/php/extensions/rabbitmq.sh +++ /dev/null @@ -1,6 +0,0 @@ -git clone --depth 1 --branch v1.11.0beta https://github.com/php-amqp/php-amqp.git -cd php-amqp -phpize -./configure -make -make install diff --git a/etc/infrastructure/php/extensions/xdebug.sh b/etc/infrastructure/php/extensions/xdebug.sh deleted file mode 100644 index 99dca7162..000000000 --- a/etc/infrastructure/php/extensions/xdebug.sh +++ /dev/null @@ -1,7 +0,0 @@ -git clone --depth 1 --branch 3.0.4 https://github.com/xdebug/xdebug.git -cd xdebug -git checkout 592ab9fa10cfa132623489511e92ef69fb91744c -phpize -./configure -make -make install 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/psalm.xml b/psalm.xml index 383336980..228b2ab7b 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,25 +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 index eced14cb2..22dfbb7ba 100644 --- a/src/Analytics/DomainEvents/Application/Store/DomainEventStorer.php +++ b/src/Analytics/DomainEvents/Application/Store/DomainEventStorer.php @@ -11,20 +11,18 @@ use CodelyTv\Analytics\DomainEvents\Domain\AnalyticsDomainEventName; use CodelyTv\Analytics\DomainEvents\Domain\DomainEventsRepository; -final class DomainEventStorer +final readonly class DomainEventStorer { - public function __construct(private readonly DomainEventsRepository $repository) - { - } + public function __construct(private DomainEventsRepository $repository) {} - public function store( - AnalyticsDomainEventId $id, - AnalyticsDomainEventAggregateId $aggregateId, - AnalyticsDomainEventName $name, - AnalyticsDomainEventBody $body - ): void { - $domainEvent = new AnalyticsDomainEvent($id, $aggregateId, $name, $body); + public function store( + AnalyticsDomainEventId $id, + AnalyticsDomainEventAggregateId $aggregateId, + AnalyticsDomainEventName $name, + AnalyticsDomainEventBody $body + ): void { + $domainEvent = new AnalyticsDomainEvent($id, $aggregateId, $name, $body); - $this->repository->save($domainEvent); - } + $this->repository->save($domainEvent); + } } diff --git a/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php b/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php index 0fc8c2806..5607d45ce 100644 --- a/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php +++ b/src/Analytics/DomainEvents/Application/Store/StoreDomainEventOnOccurred.php @@ -11,24 +11,22 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEvent; use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; -final class StoreDomainEventOnOccurred implements DomainEventSubscriber +final readonly class StoreDomainEventOnOccurred implements DomainEventSubscriber { - public function __construct(private readonly DomainEventStorer $storer) - { - } + public function __construct(private DomainEventStorer $storer) {} - public static function subscribedTo(): array - { - return [DomainEvent::class]; - } + public static function subscribedTo(): array + { + return [DomainEvent::class]; + } - public function __invoke(DomainEvent $event): void - { - $id = new AnalyticsDomainEventId($event->eventId()); - $aggregateId = new AnalyticsDomainEventAggregateId($event->aggregateId()); - $name = new AnalyticsDomainEventName($event::eventName()); - $body = new AnalyticsDomainEventBody($event->toPrimitives()); + public function __invoke(DomainEvent $event): void + { + $id = new AnalyticsDomainEventId($event->eventId()); + $aggregateId = new AnalyticsDomainEventAggregateId($event->aggregateId()); + $name = new AnalyticsDomainEventName($event::eventName()); + $body = new AnalyticsDomainEventBody($event->toPrimitives()); - $this->storer->store($id, $aggregateId, $name, $body); - } + $this->storer->store($id, $aggregateId, $name, $body); + } } diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php index 8c7447ef1..db3fe917e 100644 --- a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEvent.php @@ -4,13 +4,12 @@ namespace CodelyTv\Analytics\DomainEvents\Domain; -final class AnalyticsDomainEvent +final readonly class AnalyticsDomainEvent { - public function __construct( - private readonly AnalyticsDomainEventId $id, - private readonly AnalyticsDomainEventAggregateId $aggregateId, - private readonly AnalyticsDomainEventName $name, - private readonly AnalyticsDomainEventBody $body - ) { - } + public function __construct( + private AnalyticsDomainEventId $id, + private AnalyticsDomainEventAggregateId $aggregateId, + private AnalyticsDomainEventName $name, + private AnalyticsDomainEventBody $body + ) {} } diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventAggregateId.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventAggregateId.php index 5b5c66a21..8d1bcdfbf 100644 --- a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventAggregateId.php +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventAggregateId.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\Uuid; -final class AnalyticsDomainEventAggregateId extends Uuid -{ -} +final class AnalyticsDomainEventAggregateId extends Uuid {} diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventBody.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventBody.php index 744dfa75b..65bbb6c03 100644 --- a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventBody.php +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventBody.php @@ -4,14 +4,12 @@ namespace CodelyTv\Analytics\DomainEvents\Domain; -final class AnalyticsDomainEventBody +final readonly class AnalyticsDomainEventBody { - public function __construct(private readonly array $value) - { - } + public function __construct(private array $value) {} - public function value(): array - { - return $this->value; - } + public function value(): array + { + return $this->value; + } } diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php index 2e186b375..6cfebcb7a 100644 --- a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventId.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\Uuid; -final class AnalyticsDomainEventId extends Uuid -{ -} +final class AnalyticsDomainEventId extends Uuid {} diff --git a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventName.php b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventName.php index 36f393995..67294de35 100644 --- a/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventName.php +++ b/src/Analytics/DomainEvents/Domain/AnalyticsDomainEventName.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class AnalyticsDomainEventName extends StringValueObject -{ -} +final class AnalyticsDomainEventName extends StringValueObject {} diff --git a/src/Analytics/DomainEvents/Domain/DomainEventsRepository.php b/src/Analytics/DomainEvents/Domain/DomainEventsRepository.php index 028572b7d..655b450d0 100644 --- a/src/Analytics/DomainEvents/Domain/DomainEventsRepository.php +++ b/src/Analytics/DomainEvents/Domain/DomainEventsRepository.php @@ -6,5 +6,5 @@ interface DomainEventsRepository { - public function save(AnalyticsDomainEvent $event): void; + public function save(AnalyticsDomainEvent $event): void; } diff --git a/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommand.php b/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommand.php index aef9a81b3..3ca7806a0 100644 --- a/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommand.php +++ b/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommand.php @@ -6,19 +6,17 @@ use CodelyTv\Shared\Domain\Bus\Command\Command; -final class AuthenticateUserCommand implements Command +final readonly class AuthenticateUserCommand implements Command { - public function __construct(private readonly string $username, private readonly string $password) - { - } + public function __construct(private string $username, private string $password) {} - public function username(): string - { - return $this->username; - } + public function username(): string + { + return $this->username; + } - public function password(): string - { - return $this->password; - } + 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 index 7c7641853..6278c023d 100644 --- a/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandler.php +++ b/src/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandler.php @@ -8,17 +8,15 @@ use CodelyTv\Backoffice\Auth\Domain\AuthUsername; use CodelyTv\Shared\Domain\Bus\Command\CommandHandler; -final class AuthenticateUserCommandHandler implements CommandHandler +final readonly class AuthenticateUserCommandHandler implements CommandHandler { - public function __construct(private readonly UserAuthenticator $authenticator) - { - } + public function __construct(private UserAuthenticator $authenticator) {} - public function __invoke(AuthenticateUserCommand $command): void - { - $username = new AuthUsername($command->username()); - $password = new AuthPassword($command->password()); + public function __invoke(AuthenticateUserCommand $command): void + { + $username = new AuthUsername($command->username()); + $password = new AuthPassword($command->password()); - $this->authenticator->authenticate($username, $password); - } + $this->authenticator->authenticate($username, $password); + } } diff --git a/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php b/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php index d86d65576..99bcf5dfe 100644 --- a/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php +++ b/src/Backoffice/Auth/Application/Authenticate/UserAuthenticator.php @@ -11,31 +11,25 @@ use CodelyTv\Backoffice\Auth\Domain\InvalidAuthCredentials; use CodelyTv\Backoffice\Auth\Domain\InvalidAuthUsername; -final class UserAuthenticator +final readonly class UserAuthenticator { - public function __construct(private readonly AuthRepository $repository) - { - } - - public function authenticate(AuthUsername $username, AuthPassword $password): void - { - $auth = $this->repository->search($username); - - $this->ensureUserExist($auth, $username); - $this->ensureCredentialsAreValid($auth, $password); - } - - private function ensureUserExist(?AuthUser $auth, AuthUsername $username): void - { - if (null === $auth) { - throw new InvalidAuthUsername($username); - } - } - - private function ensureCredentialsAreValid(AuthUser $auth, AuthPassword $password): void - { - if (!$auth->passwordMatches($password)) { - throw new InvalidAuthCredentials($auth->username()); - } - } + public function __construct(private AuthRepository $repository) {} + + public function authenticate(AuthUsername $username, AuthPassword $password): void + { + $auth = $this->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 index 9953d265f..ec76483a7 100644 --- a/src/Backoffice/Auth/Domain/AuthPassword.php +++ b/src/Backoffice/Auth/Domain/AuthPassword.php @@ -8,8 +8,8 @@ final class AuthPassword extends StringValueObject { - public function isEquals(AuthPassword $other): bool - { - return $this->value() === $other->value(); - } + public function isEquals(self $other): bool + { + return $this->value() === $other->value(); + } } diff --git a/src/Backoffice/Auth/Domain/AuthRepository.php b/src/Backoffice/Auth/Domain/AuthRepository.php index ac1bf2334..49088ecd2 100644 --- a/src/Backoffice/Auth/Domain/AuthRepository.php +++ b/src/Backoffice/Auth/Domain/AuthRepository.php @@ -6,5 +6,5 @@ interface AuthRepository { - public function search(AuthUsername $username): ?AuthUser; + public function search(AuthUsername $username): ?AuthUser; } diff --git a/src/Backoffice/Auth/Domain/AuthUser.php b/src/Backoffice/Auth/Domain/AuthUser.php index 1e951c905..5be0b4b51 100644 --- a/src/Backoffice/Auth/Domain/AuthUser.php +++ b/src/Backoffice/Auth/Domain/AuthUser.php @@ -4,19 +4,17 @@ namespace CodelyTv\Backoffice\Auth\Domain; -final class AuthUser +final readonly class AuthUser { - public function __construct(private readonly AuthUsername $username, private readonly AuthPassword $password) - { - } + public function __construct(private AuthUsername $username, private AuthPassword $password) {} - public function passwordMatches(AuthPassword $password): bool - { - return $this->password->isEquals($password); - } + public function passwordMatches(AuthPassword $password): bool + { + return $this->password->isEquals($password); + } - public function username(): AuthUsername - { - return $this->username; - } + public function username(): AuthUsername + { + return $this->username; + } } diff --git a/src/Backoffice/Auth/Domain/AuthUsername.php b/src/Backoffice/Auth/Domain/AuthUsername.php index 2841fa3a4..bc1f5b884 100644 --- a/src/Backoffice/Auth/Domain/AuthUsername.php +++ b/src/Backoffice/Auth/Domain/AuthUsername.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class AuthUsername extends StringValueObject -{ -} +final class AuthUsername extends StringValueObject {} diff --git a/src/Backoffice/Auth/Domain/InvalidAuthCredentials.php b/src/Backoffice/Auth/Domain/InvalidAuthCredentials.php index b5a87059f..1854118bd 100644 --- a/src/Backoffice/Auth/Domain/InvalidAuthCredentials.php +++ b/src/Backoffice/Auth/Domain/InvalidAuthCredentials.php @@ -8,8 +8,8 @@ final class InvalidAuthCredentials extends RuntimeException { - public function __construct(AuthUsername $username) - { - parent::__construct(sprintf('The credentials for <%s> are invalid', $username->value())); - } + public function __construct(AuthUsername $username) + { + parent::__construct(sprintf('The credentials for <%s> are invalid', $username->value())); + } } diff --git a/src/Backoffice/Auth/Domain/InvalidAuthUsername.php b/src/Backoffice/Auth/Domain/InvalidAuthUsername.php index c00e0b5f3..b120207cb 100644 --- a/src/Backoffice/Auth/Domain/InvalidAuthUsername.php +++ b/src/Backoffice/Auth/Domain/InvalidAuthUsername.php @@ -8,8 +8,8 @@ final class InvalidAuthUsername extends RuntimeException { - public function __construct(AuthUsername $username) - { - parent::__construct(sprintf('The user <%s> does not exists', $username->value())); - } + public function __construct(AuthUsername $username) + { + parent::__construct(sprintf('The user <%s> does not exists', $username->value())); + } } diff --git a/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php b/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php index d1645e452..dea981f8a 100644 --- a/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php +++ b/src/Backoffice/Auth/Infrastructure/Persistence/InMemoryAuthRepository.php @@ -8,19 +8,20 @@ use CodelyTv\Backoffice\Auth\Domain\AuthRepository; use CodelyTv\Backoffice\Auth\Domain\AuthUser; use CodelyTv\Backoffice\Auth\Domain\AuthUsername; + use function Lambdish\Phunctional\get; final class InMemoryAuthRepository implements AuthRepository { - private const USERS = [ - 'javi' => 'barbitas', - 'rafa' => 'pelazo', - ]; + private const USERS = [ + 'javi' => 'barbitas', + 'rafa' => 'pelazo', + ]; - public function search(AuthUsername $username): ?AuthUser - { - $password = get($username->value(), self::USERS); + public function search(AuthUsername $username): ?AuthUser + { + $password = get($username->value(), self::USERS); - return null !== $password ? new AuthUser($username, new AuthPassword($password)) : null; - } + 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 index 7c8440ba6..d7984b858 100644 --- a/src/Backoffice/Courses/Application/BackofficeCourseResponse.php +++ b/src/Backoffice/Courses/Application/BackofficeCourseResponse.php @@ -4,24 +4,22 @@ namespace CodelyTv\Backoffice\Courses\Application; -final class BackofficeCourseResponse +final readonly class BackofficeCourseResponse { - public function __construct(private readonly string $id, private readonly string $name, private readonly string $duration) - { - } + public function __construct(private string $id, private string $name, private string $duration) {} - public function id(): string - { - return $this->id; - } + public function id(): string + { + return $this->id; + } - public function name(): string - { - return $this->name; - } + public function name(): string + { + return $this->name; + } - public function duration(): string - { - return $this->duration; - } + public function duration(): string + { + return $this->duration; + } } diff --git a/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php b/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php index 973202782..489841794 100644 --- a/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php +++ b/src/Backoffice/Courses/Application/BackofficeCoursesResponse.php @@ -8,15 +8,15 @@ final class BackofficeCoursesResponse implements Response { - private readonly array $courses; + private readonly array $courses; - public function __construct(BackofficeCourseResponse ...$courses) - { - $this->courses = $courses; - } + public function __construct(BackofficeCourseResponse ...$courses) + { + $this->courses = $courses; + } - public function courses(): array - { - return $this->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 index 9fb967455..8493f6a46 100644 --- a/src/Backoffice/Courses/Application/Create/BackofficeCourseCreator.php +++ b/src/Backoffice/Courses/Application/Create/BackofficeCourseCreator.php @@ -7,14 +7,12 @@ use CodelyTv\Backoffice\Courses\Domain\BackofficeCourse; use CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository; -final class BackofficeCourseCreator +final readonly class BackofficeCourseCreator { - public function __construct(private readonly BackofficeCourseRepository $repository) - { - } + public function __construct(private BackofficeCourseRepository $repository) {} - public function create(string $id, string $name, string $duration): void - { - $this->repository->save(new BackofficeCourse($id, $name, $duration)); - } + public function create(string $id, string $name, string $duration): void + { + $this->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 index a7ca626ce..06ecd9541 100644 --- a/src/Backoffice/Courses/Application/Create/CreateBackofficeCourseOnCourseCreated.php +++ b/src/Backoffice/Courses/Application/Create/CreateBackofficeCourseOnCourseCreated.php @@ -7,19 +7,17 @@ use CodelyTv\Mooc\Courses\Domain\CourseCreatedDomainEvent; use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; -final class CreateBackofficeCourseOnCourseCreated implements DomainEventSubscriber +final readonly class CreateBackofficeCourseOnCourseCreated implements DomainEventSubscriber { - public function __construct(private readonly BackofficeCourseCreator $creator) - { - } + public function __construct(private BackofficeCourseCreator $creator) {} - public static function subscribedTo(): array - { - return [CourseCreatedDomainEvent::class]; - } + public static function subscribedTo(): array + { + return [CourseCreatedDomainEvent::class]; + } - public function __invoke(CourseCreatedDomainEvent $event): void - { - $this->creator->create($event->aggregateId(), $event->name(), $event->duration()); - } + public function __invoke(CourseCreatedDomainEvent $event): void + { + $this->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 index 0045c8e4c..8cb541d64 100644 --- a/src/Backoffice/Courses/Application/SearchAll/AllBackofficeCoursesSearcher.php +++ b/src/Backoffice/Courses/Application/SearchAll/AllBackofficeCoursesSearcher.php @@ -8,25 +8,24 @@ use CodelyTv\Backoffice\Courses\Application\BackofficeCoursesResponse; use CodelyTv\Backoffice\Courses\Domain\BackofficeCourse; use CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository; + use function Lambdish\Phunctional\map; -final class AllBackofficeCoursesSearcher +final readonly class AllBackofficeCoursesSearcher { - public function __construct(private readonly BackofficeCourseRepository $repository) - { - } + public function __construct(private BackofficeCourseRepository $repository) {} - public function searchAll(): BackofficeCoursesResponse - { - return new BackofficeCoursesResponse(...map($this->toResponse(), $this->repository->searchAll())); - } + public function searchAll(): BackofficeCoursesResponse + { + return new BackofficeCoursesResponse(...map($this->toResponse(), $this->repository->searchAll())); + } - private function toResponse(): callable - { - return static fn (BackofficeCourse $course) => new BackofficeCourseResponse( - $course->id(), - $course->name(), - $course->duration() - ); - } + 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 index 1d537951b..41df25386 100644 --- a/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQuery.php +++ b/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQuery.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; -final class SearchAllBackofficeCoursesQuery implements Query -{ -} +final class SearchAllBackofficeCoursesQuery implements Query {} diff --git a/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQueryHandler.php b/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQueryHandler.php index dde35f54f..552bb73a5 100644 --- a/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQueryHandler.php +++ b/src/Backoffice/Courses/Application/SearchAll/SearchAllBackofficeCoursesQueryHandler.php @@ -7,14 +7,12 @@ use CodelyTv\Backoffice\Courses\Application\BackofficeCoursesResponse; use CodelyTv\Shared\Domain\Bus\Query\QueryHandler; -final class SearchAllBackofficeCoursesQueryHandler implements QueryHandler +final readonly class SearchAllBackofficeCoursesQueryHandler implements QueryHandler { - public function __construct(private readonly AllBackofficeCoursesSearcher $searcher) - { - } + public function __construct(private AllBackofficeCoursesSearcher $searcher) {} - public function __invoke(SearchAllBackofficeCoursesQuery $query): BackofficeCoursesResponse - { - return $this->searcher->searchAll(); - } + public function __invoke(SearchAllBackofficeCoursesQuery $query): BackofficeCoursesResponse + { + return $this->searcher->searchAll(); + } } diff --git a/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php b/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php index 74cdc7372..4ed2280a4 100644 --- a/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php +++ b/src/Backoffice/Courses/Application/SearchByCriteria/BackofficeCoursesByCriteriaSearcher.php @@ -11,27 +11,26 @@ use CodelyTv\Shared\Domain\Criteria\Criteria; use CodelyTv\Shared\Domain\Criteria\Filters; use CodelyTv\Shared\Domain\Criteria\Order; + use function Lambdish\Phunctional\map; -final class BackofficeCoursesByCriteriaSearcher +final readonly class BackofficeCoursesByCriteriaSearcher { - public function __construct(private readonly BackofficeCourseRepository $repository) - { - } - - public function search(Filters $filters, Order $order, ?int $limit, ?int $offset): BackofficeCoursesResponse - { - $criteria = new Criteria($filters, $order, $offset, $limit); - - return new BackofficeCoursesResponse(...map($this->toResponse(), $this->repository->matching($criteria))); - } - - private function toResponse(): callable - { - return static fn (BackofficeCourse $course) => new BackofficeCourseResponse( - $course->id(), - $course->name(), - $course->duration() - ); - } + public function __construct(private BackofficeCourseRepository $repository) {} + + public function search(Filters $filters, Order $order, ?int $limit, ?int $offset): BackofficeCoursesResponse + { + $criteria = new Criteria($filters, $order, $offset, $limit); + + return new BackofficeCoursesResponse(...map($this->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 index f35fb7089..3833cd4e5 100644 --- a/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQuery.php +++ b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQuery.php @@ -6,39 +6,38 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; -final class SearchBackofficeCoursesByCriteriaQuery implements Query +final readonly class SearchBackofficeCoursesByCriteriaQuery implements Query { - public function __construct( - private readonly array $filters, - private readonly ?string $orderBy, - private readonly ?string $order, - private readonly ?int $limit, - private readonly ?int $offset - ) { - } - - public function filters(): array - { - return $this->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; - } + public function __construct( + private array $filters, + private ?string $orderBy, + private ?string $order, + private ?int $limit, + private ?int $offset + ) {} + + public function filters(): array + { + return $this->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 index a3cfe6f71..9be731db6 100644 --- a/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQueryHandler.php +++ b/src/Backoffice/Courses/Application/SearchByCriteria/SearchBackofficeCoursesByCriteriaQueryHandler.php @@ -9,17 +9,15 @@ use CodelyTv\Shared\Domain\Criteria\Filters; use CodelyTv\Shared\Domain\Criteria\Order; -final class SearchBackofficeCoursesByCriteriaQueryHandler implements QueryHandler +final readonly class SearchBackofficeCoursesByCriteriaQueryHandler implements QueryHandler { - public function __construct(private readonly BackofficeCoursesByCriteriaSearcher $searcher) - { - } + public function __construct(private BackofficeCoursesByCriteriaSearcher $searcher) {} - public function __invoke(SearchBackofficeCoursesByCriteriaQuery $query): BackofficeCoursesResponse - { - $filters = Filters::fromValues($query->filters()); - $order = Order::fromValues($query->orderBy(), $query->order()); + public function __invoke(SearchBackofficeCoursesByCriteriaQuery $query): BackofficeCoursesResponse + { + $filters = Filters::fromValues($query->filters()); + $order = Order::fromValues($query->orderBy(), $query->order()); - return $this->searcher->search($filters, $order, $query->limit(), $query->offset()); - } + 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 index 96636c2d0..85bc4ea53 100644 --- a/src/Backoffice/Courses/Domain/BackofficeCourse.php +++ b/src/Backoffice/Courses/Domain/BackofficeCourse.php @@ -8,36 +8,34 @@ final class BackofficeCourse extends AggregateRoot { - public function __construct(private readonly string $id, private readonly string $name, private readonly string $duration) - { - } - - public static function fromPrimitives(array $primitives): BackofficeCourse - { - return new self($primitives['id'], $primitives['name'], $primitives['duration']); - } - - public function toPrimitives(): array - { - return [ - 'id' => $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; - } + public function __construct(private readonly string $id, private readonly string $name, private readonly string $duration) {} + + public static function fromPrimitives(array $primitives): self + { + return new self($primitives['id'], $primitives['name'], $primitives['duration']); + } + + public function toPrimitives(): array + { + return [ + 'id' => $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 index 1f8d82c40..815a0a6b3 100644 --- a/src/Backoffice/Courses/Domain/BackofficeCourseRepository.php +++ b/src/Backoffice/Courses/Domain/BackofficeCourseRepository.php @@ -8,9 +8,9 @@ interface BackofficeCourseRepository { - public function save(BackofficeCourse $course): void; + public function save(BackofficeCourse $course): void; - public function searchAll(): array; + public function searchAll(): array; - public function matching(Criteria $criteria): array; + public function matching(Criteria $criteria): array; } diff --git a/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php b/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php index 5bd3abbec..3a359ce41 100644 --- a/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php +++ b/src/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepository.php @@ -8,32 +8,33 @@ use CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository; use CodelyTv\Shared\Domain\Criteria\Criteria; use CodelyTv\Shared\Infrastructure\Persistence\Elasticsearch\ElasticsearchRepository; + use function Lambdish\Phunctional\map; final class ElasticsearchBackofficeCourseRepository extends ElasticsearchRepository implements BackofficeCourseRepository { - public function save(BackofficeCourse $course): void - { - $this->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::fromPrimitives($primitives); - } + public function save(BackofficeCourse $course): void + { + $this->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 index 5a2062beb..d2d84c341 100644 --- a/src/Backoffice/Courses/Infrastructure/Persistence/InMemoryCacheBackofficeCourseRepository.php +++ b/src/Backoffice/Courses/Infrastructure/Persistence/InMemoryCacheBackofficeCourseRepository.php @@ -7,39 +7,38 @@ use CodelyTv\Backoffice\Courses\Domain\BackofficeCourse; use CodelyTv\Backoffice\Courses\Domain\BackofficeCourseRepository; use CodelyTv\Shared\Domain\Criteria\Criteria; + use function Lambdish\Phunctional\get; final class InMemoryCacheBackofficeCourseRepository implements BackofficeCourseRepository { - private static array $allCoursesCache = []; - private static array $matchingCache = []; - - public function __construct(private readonly BackofficeCourseRepository $repository) - { - } - - public function save(BackofficeCourse $course): void - { - $this->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); - } + private static array $allCoursesCache = []; + private static array $matchingCache = []; + + public function __construct(private readonly BackofficeCourseRepository $repository) {} + + public function save(BackofficeCourse $course): void + { + $this->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 index 8203a7fee..46891200b 100644 --- a/src/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepository.php +++ b/src/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepository.php @@ -12,20 +12,20 @@ final class MySqlBackofficeCourseRepository extends DoctrineRepository implements BackofficeCourseRepository { - public function save(BackofficeCourse $course): void - { - $this->persist($course); - } + public function save(BackofficeCourse $course): void + { + $this->persist($course); + } - public function searchAll(): array - { - return $this->repository(BackofficeCourse::class)->findAll(); - } + public function searchAll(): array + { + return $this->repository(BackofficeCourse::class)->findAll(); + } - public function matching(Criteria $criteria): array - { - $doctrineCriteria = DoctrineCriteriaConverter::convert($criteria); + public function matching(Criteria $criteria): array + { + $doctrineCriteria = DoctrineCriteriaConverter::convert($criteria); - return $this->repository(BackofficeCourse::class)->matching($doctrineCriteria)->toArray(); - } + return $this->repository(BackofficeCourse::class)->matching($doctrineCriteria)->toArray(); + } } diff --git a/src/Mooc/Courses/Application/Create/CourseCreator.php b/src/Mooc/Courses/Application/Create/CourseCreator.php index b85373945..12b07bed4 100644 --- a/src/Mooc/Courses/Application/Create/CourseCreator.php +++ b/src/Mooc/Courses/Application/Create/CourseCreator.php @@ -11,17 +11,15 @@ use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; use CodelyTv\Shared\Domain\Bus\Event\EventBus; -final class CourseCreator +final readonly class CourseCreator { - public function __construct(private readonly CourseRepository $repository, private readonly EventBus $bus) - { - } + public function __construct(private CourseRepository $repository, private EventBus $bus) {} - public function __invoke(CourseId $id, CourseName $name, CourseDuration $duration): void - { - $course = Course::create($id, $name, $duration); + public function __invoke(CourseId $id, CourseName $name, CourseDuration $duration): void + { + $course = Course::create($id, $name, $duration); - $this->repository->save($course); - $this->bus->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 970c9cdaf..e710c86a9 100644 --- a/src/Mooc/Courses/Application/Create/CreateCourseCommand.php +++ b/src/Mooc/Courses/Application/Create/CreateCourseCommand.php @@ -6,24 +6,22 @@ use CodelyTv\Shared\Domain\Bus\Command\Command; -final class CreateCourseCommand implements Command +final readonly class CreateCourseCommand implements Command { - public function __construct(private readonly string $id, private readonly string $name, private readonly string $duration) - { - } + public function __construct(private string $id, private string $name, private string $duration) {} - public function id(): string - { - return $this->id; - } + public function id(): string + { + return $this->id; + } - public function name(): string - { - return $this->name; - } + public function name(): string + { + return $this->name; + } - public function duration(): string - { - return $this->duration; - } + 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 49b2eaf66..e4685c5cb 100644 --- a/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php +++ b/src/Mooc/Courses/Application/Create/CreateCourseCommandHandler.php @@ -9,18 +9,16 @@ use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; use CodelyTv\Shared\Domain\Bus\Command\CommandHandler; -final class CreateCourseCommandHandler implements CommandHandler +final readonly class CreateCourseCommandHandler implements CommandHandler { - public function __construct(private readonly CourseCreator $creator) - { - } + public function __construct(private CourseCreator $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()); + $name = new CourseName($command->name()); + $duration = new CourseDuration($command->duration()); - $this->creator->__invoke($id, $name, $duration); - } + $this->creator->__invoke($id, $name, $duration); + } } diff --git a/src/Mooc/Courses/Application/Find/CourseFinder.php b/src/Mooc/Courses/Application/Find/CourseFinder.php index b307b9d37..695e2aef7 100644 --- a/src/Mooc/Courses/Application/Find/CourseFinder.php +++ b/src/Mooc/Courses/Application/Find/CourseFinder.php @@ -9,20 +9,18 @@ use CodelyTv\Mooc\Courses\Domain\CourseRepository; use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; -final class CourseFinder +final readonly class CourseFinder { - public function __construct(private readonly CourseRepository $repository) - { - } + public function __construct(private CourseRepository $repository) {} - public function __invoke(CourseId $id): Course - { - $course = $this->repository->search($id); + public function __invoke(CourseId $id): Course + { + $course = $this->repository->search($id); - if (null === $course) { - throw new CourseNotExist($id); - } + if ($course === null) { + throw new CourseNotExist($id); + } - return $course; - } + return $course; + } } diff --git a/src/Mooc/Courses/Application/Update/CourseRenamer.php b/src/Mooc/Courses/Application/Update/CourseRenamer.php index e1916c4f4..77c8632d4 100644 --- a/src/Mooc/Courses/Application/Update/CourseRenamer.php +++ b/src/Mooc/Courses/Application/Update/CourseRenamer.php @@ -10,22 +10,22 @@ use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; use CodelyTv\Shared\Domain\Bus\Event\EventBus; -final class CourseRenamer +final readonly class CourseRenamer { - private readonly CourseFinder $finder; + private CourseFinder $finder; - public function __construct(private readonly CourseRepository $repository, private readonly EventBus $bus) - { - $this->finder = new CourseFinder($repository); - } + public function __construct(private CourseRepository $repository, private EventBus $bus) + { + $this->finder = new CourseFinder($repository); + } - public function __invoke(CourseId $id, CourseName $newName): void - { - $course = $this->finder->__invoke($id); + public function __invoke(CourseId $id, CourseName $newName): void + { + $course = $this->finder->__invoke($id); - $course->rename($newName); + $course->rename($newName); - $this->repository->save($course); - $this->bus->publish(...$course->pullDomainEvents()); - } + $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 f41041490..854ecdd4e 100644 --- a/src/Mooc/Courses/Domain/Course.php +++ b/src/Mooc/Courses/Domain/Course.php @@ -9,36 +9,34 @@ final class Course extends AggregateRoot { - 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; - } + 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 41a68e6ec..69e89f799 100644 --- a/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php +++ b/src/Mooc/Courses/Domain/CourseCreatedDomainEvent.php @@ -8,45 +8,45 @@ final class CourseCreatedDomainEvent extends DomainEvent { - 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; - } + 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/CourseDuration.php b/src/Mooc/Courses/Domain/CourseDuration.php index 3de808b53..71a56484b 100644 --- a/src/Mooc/Courses/Domain/CourseDuration.php +++ b/src/Mooc/Courses/Domain/CourseDuration.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class CourseDuration extends StringValueObject -{ -} +final class CourseDuration extends StringValueObject {} diff --git a/src/Mooc/Courses/Domain/CourseName.php b/src/Mooc/Courses/Domain/CourseName.php index 52855e4b2..2235c0996 100644 --- a/src/Mooc/Courses/Domain/CourseName.php +++ b/src/Mooc/Courses/Domain/CourseName.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class CourseName extends StringValueObject -{ -} +final class CourseName extends StringValueObject {} diff --git a/src/Mooc/Courses/Domain/CourseNotExist.php b/src/Mooc/Courses/Domain/CourseNotExist.php index ab3e95e4d..c91f35340 100644 --- a/src/Mooc/Courses/Domain/CourseNotExist.php +++ b/src/Mooc/Courses/Domain/CourseNotExist.php @@ -9,18 +9,18 @@ final class CourseNotExist extends DomainError { - public function __construct(private readonly CourseId $id) - { - parent::__construct(); - } + public function __construct(private readonly CourseId $id) + { + parent::__construct(); + } - public function errorCode(): string - { - return 'course_not_exist'; - } + public function errorCode(): string + { + return 'course_not_exist'; + } - protected function errorMessage(): string - { - return sprintf('The course <%s> does not exist', $this->id->value()); - } + protected function errorMessage(): string + { + return sprintf('The course <%s> does not exist', $this->id->value()); + } } diff --git a/src/Mooc/Courses/Domain/CourseRepository.php b/src/Mooc/Courses/Domain/CourseRepository.php index bf12fad1b..338ad9500 100644 --- a/src/Mooc/Courses/Domain/CourseRepository.php +++ b/src/Mooc/Courses/Domain/CourseRepository.php @@ -8,7 +8,7 @@ interface CourseRepository { - public function save(Course $course): void; + public function save(Course $course): void; - public function search(CourseId $id): ?Course; + public function search(CourseId $id): ?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 @@ +persist($course); - } + public function save(Course $course): void + { + $this->persist($course); + } - public function search(CourseId $id): ?Course - { - return $this->repository(Course::class)->find($id); - } + 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 index 05124081d..b6a15ecbd 100644 --- a/src/Mooc/Courses/Infrastructure/Persistence/FileCourseRepository.php +++ b/src/Mooc/Courses/Infrastructure/Persistence/FileCourseRepository.php @@ -10,22 +10,22 @@ final class FileCourseRepository implements CourseRepository { - private const FILE_PATH = __DIR__ . '/courses'; + private const FILE_PATH = __DIR__ . '/courses'; - public function save(Course $course): void - { - file_put_contents($this->fileName($course->id()->value()), serialize($course)); - } + public function save(Course $course): void + { + file_put_contents($this->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; - } + 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); - } + 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 index a0e5c2996..000422643 100644 --- a/src/Mooc/CoursesCounter/Application/Find/CoursesCounterFinder.php +++ b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterFinder.php @@ -7,20 +7,18 @@ use CodelyTv\Mooc\CoursesCounter\Domain\CoursesCounterNotExist; use CodelyTv\Mooc\CoursesCounter\Domain\CoursesCounterRepository; -final class CoursesCounterFinder +final readonly class CoursesCounterFinder { - public function __construct(private readonly CoursesCounterRepository $repository) - { - } + public function __construct(private CoursesCounterRepository $repository) {} - public function __invoke(): CoursesCounterResponse - { - $counter = $this->repository->search(); + public function __invoke(): CoursesCounterResponse + { + $counter = $this->repository->search(); - if (null === $counter) { - throw new CoursesCounterNotExist(); - } + if ($counter === null) { + throw new CoursesCounterNotExist(); + } - return new CoursesCounterResponse($counter->total()->value()); - } + return new CoursesCounterResponse($counter->total()->value()); + } } diff --git a/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php index b0373e28e..21e8a0811 100644 --- a/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php +++ b/src/Mooc/CoursesCounter/Application/Find/CoursesCounterResponse.php @@ -6,14 +6,12 @@ use CodelyTv\Shared\Domain\Bus\Query\Response; -final class CoursesCounterResponse implements Response +final readonly class CoursesCounterResponse implements Response { - public function __construct(private readonly int $total) - { - } + public function __construct(private int $total) {} - public function total(): int - { - return $this->total; - } + public function total(): int + { + return $this->total; + } } diff --git a/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php index a13b1e9ea..4be728c05 100644 --- a/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php +++ b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQuery.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; -final class FindCoursesCounterQuery implements Query -{ -} +final class FindCoursesCounterQuery implements Query {} diff --git a/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandler.php b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandler.php index b7808f602..cedb24b1c 100644 --- a/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandler.php +++ b/src/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandler.php @@ -6,14 +6,12 @@ use CodelyTv\Shared\Domain\Bus\Query\QueryHandler; -final class FindCoursesCounterQueryHandler implements QueryHandler +final readonly class FindCoursesCounterQueryHandler implements QueryHandler { - public function __construct(private readonly CoursesCounterFinder $finder) - { - } + public function __construct(private CoursesCounterFinder $finder) {} - public function __invoke(FindCoursesCounterQuery $query): CoursesCounterResponse - { - return $this->finder->__invoke(); - } + public function __invoke(FindCoursesCounterQuery $query): CoursesCounterResponse + { + return $this->finder->__invoke(); + } } diff --git a/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php b/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php index 0da54ad77..b1131d11d 100644 --- a/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php +++ b/src/Mooc/CoursesCounter/Application/Increment/CoursesCounterIncrementer.php @@ -11,29 +11,28 @@ use CodelyTv\Shared\Domain\Bus\Event\EventBus; use CodelyTv\Shared\Domain\UuidGenerator; -final class CoursesCounterIncrementer +final readonly class CoursesCounterIncrementer { - public function __construct( - private readonly CoursesCounterRepository $repository, - private readonly UuidGenerator $uuidGenerator, - private readonly EventBus $bus - ) { - } - - public function __invoke(CourseId $courseId): void - { - $counter = $this->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())); - } + public function __construct( + private CoursesCounterRepository $repository, + private UuidGenerator $uuidGenerator, + private EventBus $bus + ) {} + + public function __invoke(CourseId $courseId): void + { + $counter = $this->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 index 8e1a83e6f..d561d7686 100644 --- a/src/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreated.php +++ b/src/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreated.php @@ -7,23 +7,22 @@ use CodelyTv\Mooc\Courses\Domain\CourseCreatedDomainEvent; use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; + use function Lambdish\Phunctional\apply; -final class IncrementCoursesCounterOnCourseCreated implements DomainEventSubscriber +final readonly class IncrementCoursesCounterOnCourseCreated implements DomainEventSubscriber { - public function __construct(private readonly CoursesCounterIncrementer $incrementer) - { - } + public function __construct(private CoursesCounterIncrementer $incrementer) {} - public static function subscribedTo(): array - { - return [CourseCreatedDomainEvent::class]; - } + public static function subscribedTo(): array + { + return [CourseCreatedDomainEvent::class]; + } - public function __invoke(CourseCreatedDomainEvent $event): void - { - $courseId = new CourseId($event->aggregateId()); + public function __invoke(CourseCreatedDomainEvent $event): void + { + $courseId = new CourseId($event->aggregateId()); - apply($this->incrementer, [$courseId]); - } + apply($this->incrementer, [$courseId]); + } } diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounter.php b/src/Mooc/CoursesCounter/Domain/CoursesCounter.php index fc73ba58b..3d89a5da0 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounter.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounter.php @@ -6,57 +6,58 @@ use CodelyTv\Mooc\Shared\Domain\Courses\CourseId; use CodelyTv\Shared\Domain\Aggregate\AggregateRoot; + use function Lambdish\Phunctional\search; final class CoursesCounter extends AggregateRoot { - private array $existingCourses; - - public function __construct( - private readonly CoursesCounterId $id, - private CoursesCounterTotal $total, - CourseId ...$existingCourses - ) { - $this->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 null !== $existingCourse; - } - - private function courseIdComparator(CourseId $courseId): callable - { - return static fn (CourseId $other) => $courseId->equals($other); - } + private array $existingCourses; + + public function __construct( + private readonly CoursesCounterId $id, + private CoursesCounterTotal $total, + CourseId ...$existingCourses + ) { + $this->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 index 9142b2594..16d0461e9 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounterId.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterId.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\Uuid; -final class CoursesCounterId extends Uuid -{ -} +final class CoursesCounterId extends Uuid {} diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEvent.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEvent.php index 4867871db..a64681dc7 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEvent.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEvent.php @@ -8,33 +8,33 @@ final class CoursesCounterIncrementedDomainEvent extends DomainEvent { - public function __construct( - string $aggregateId, - private readonly int $total, - string $eventId = null, - string $occurredOn = null - ) { - parent::__construct($aggregateId, $eventId, $occurredOn); - } + public function __construct( + string $aggregateId, + private readonly int $total, + string $eventId = null, + string $occurredOn = null + ) { + parent::__construct($aggregateId, $eventId, $occurredOn); + } - public static function eventName(): string - { - return 'courses_counter.incremented'; - } + public static function eventName(): string + { + return 'courses_counter.incremented'; + } - public static function fromPrimitives( - string $aggregateId, - array $body, - string $eventId, - string $occurredOn - ): DomainEvent { - return new self($aggregateId, $body['total'], $eventId, $occurredOn); - } + public static function fromPrimitives( + string $aggregateId, + array $body, + string $eventId, + string $occurredOn + ): DomainEvent { + return new self($aggregateId, $body['total'], $eventId, $occurredOn); + } - public function toPrimitives(): array - { - return [ - 'total' => $this->total, - ]; - } + public function toPrimitives(): array + { + return [ + 'total' => $this->total, + ]; + } } diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php index 5650c9267..a882cdc38 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterNotExist.php @@ -8,8 +8,8 @@ final class CoursesCounterNotExist extends RuntimeException { - public function __construct() - { - parent::__construct('The courses counter not exist'); - } + public function __construct() + { + parent::__construct('The courses counter not exist'); + } } diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterRepository.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterRepository.php index 4dcd67d05..d2f875b70 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounterRepository.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterRepository.php @@ -6,7 +6,7 @@ interface CoursesCounterRepository { - public function save(CoursesCounter $counter): void; + public function save(CoursesCounter $counter): void; - public function search(): ?CoursesCounter; + public function search(): ?CoursesCounter; } diff --git a/src/Mooc/CoursesCounter/Domain/CoursesCounterTotal.php b/src/Mooc/CoursesCounter/Domain/CoursesCounterTotal.php index 619ca19bb..6446125fa 100644 --- a/src/Mooc/CoursesCounter/Domain/CoursesCounterTotal.php +++ b/src/Mooc/CoursesCounter/Domain/CoursesCounterTotal.php @@ -8,13 +8,13 @@ final class CoursesCounterTotal extends IntValueObject { - public static function initialize(): self - { - return new self(0); - } + public static function initialize(): self + { + return new self(0); + } - public function increment(): self - { - return new self($this->value() + 1); - } + public function increment(): self + { + return new self($this->value() + 1); + } } diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php index 7a300a2bc..6a2c5d95a 100644 --- a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseCounterIdType.php @@ -9,8 +9,8 @@ final class CourseCounterIdType extends UuidType { - protected function typeClassName(): string - { - return CoursesCounterId::class; - } + protected function typeClassName(): string + { + return CoursesCounterId::class; + } } diff --git a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseIdsType.php b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseIdsType.php index 40bc8611f..71c98ac5c 100644 --- a/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseIdsType.php +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/Doctrine/CourseIdsType.php @@ -8,29 +8,30 @@ use CodelyTv\Shared\Infrastructure\Doctrine\Dbal\DoctrineCustomType; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\JsonType; + use function Lambdish\Phunctional\map; final class CourseIdsType extends JsonType implements DoctrineCustomType { - public static function customTypeName(): string - { - return 'course_ids'; - } - - public function getName(): string - { - return self::customTypeName(); - } - - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - return parent::convertToDatabaseValue(map(fn (CourseId $id) => $id->value(), $value), $platform); - } - - public function convertToPHPValue($value, AbstractPlatform $platform) - { - $scalars = parent::convertToPHPValue($value, $platform); - - return map(fn (string $value) => new CourseId($value), $scalars); - } + public static function customTypeName(): string + { + return 'course_ids'; + } + + public function getName(): string + { + return self::customTypeName(); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string + { + return parent::convertToDatabaseValue(map(fn (CourseId $id): string => $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/DoctrineCoursesCounterRepository.php b/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php index 2de9f4ebc..64622b072 100644 --- a/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php +++ b/src/Mooc/CoursesCounter/Infrastructure/Persistence/DoctrineCoursesCounterRepository.php @@ -10,13 +10,13 @@ final class DoctrineCoursesCounterRepository extends DoctrineRepository implements CoursesCounterRepository { - public function save(CoursesCounter $counter): void - { - $this->persist($counter); - } + public function save(CoursesCounter $counter): void + { + $this->persist($counter); + } - public function search(): ?CoursesCounter - { - return $this->repository(CoursesCounter::class)->findOneBy([]); - } + public function search(): ?CoursesCounter + { + return $this->repository(CoursesCounter::class)->findOneBy([]); + } } diff --git a/src/Mooc/Shared/Domain/Courses/CourseId.php b/src/Mooc/Shared/Domain/Courses/CourseId.php index ed2c5a753..ff6b551a8 100644 --- a/src/Mooc/Shared/Domain/Courses/CourseId.php +++ b/src/Mooc/Shared/Domain/Courses/CourseId.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\Uuid; -final class CourseId extends Uuid -{ -} +final class CourseId extends Uuid {} diff --git a/src/Mooc/Shared/Domain/Videos/VideoUrl.php b/src/Mooc/Shared/Domain/Videos/VideoUrl.php index 3bc0448ea..bb5d099d4 100644 --- a/src/Mooc/Shared/Domain/Videos/VideoUrl.php +++ b/src/Mooc/Shared/Domain/Videos/VideoUrl.php @@ -9,17 +9,17 @@ final class VideoUrl extends StringValueObject { - public function __construct(string $value) - { - $this->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 index 436a867d5..28823de48 100644 --- a/src/Mooc/Shared/Infrastructure/Doctrine/DbalTypesSearcher.php +++ b/src/Mooc/Shared/Infrastructure/Doctrine/DbalTypesSearcher.php @@ -4,70 +4,66 @@ namespace CodelyTv\Mooc\Shared\Infrastructure\Doctrine; -use CodelyTv\Shared\Domain\Utils; use function Lambdish\Phunctional\filter; use function Lambdish\Phunctional\map; use function Lambdish\Phunctional\reduce; final class DbalTypesSearcher { - private const MAPPINGS_PATH = 'Infrastructure/Persistence/Doctrine'; + private const MAPPINGS_PATH = 'Infrastructure/Persistence/Doctrine'; - public static function inPath(string $path, string $contextName): array - { - $possibleDbalDirectories = self::possibleDbalPaths($path); - $dbalDirectories = filter(self::isExistingDbalPath(), $possibleDbalDirectories); + public static function inPath(string $path, string $contextName): array + { + $possibleDbalDirectories = self::possibleDbalPaths($path); + $dbalDirectories = filter(self::isExistingDbalPath(), $possibleDbalDirectories); - return reduce(self::dbalClassesSearcher($contextName), $dbalDirectories, []); - } + return reduce(self::dbalClassesSearcher($contextName), $dbalDirectories, []); + } - private static function modulesInPath(string $path): array - { - return filter( - static fn (string $possibleModule) => !in_array($possibleModule, ['.', '..'], true), - scandir($path) - ); - } + private static function modulesInPath(string $path): array + { + return filter( + static fn (string $possibleModule): bool => !in_array($possibleModule, ['.', '..'], true), + scandir($path) + ); + } - private static function possibleDbalPaths(string $path): array - { - return map( - static function ($unused, string $module) use ($path) { - $mappingsPath = self::MAPPINGS_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)) - ); - } + return realpath("$path/$module/$mappingsPath"); + }, + array_flip(self::modulesInPath($path)) + ); + } - private static function isExistingDbalPath(): callable - { - return static fn (string $path) => !empty($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) { - $possibleFiles = scandir($path); - $files = filter( - static fn ($file) => Utils::endsWith('Type.php', $file), - $possibleFiles - ); + 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) { - $fullPath = "$path/$file"; - $splittedPath = explode("/src/$contextName/", $fullPath); + $namespaces = map( + static function (string $file) use ($path, $contextName): string { + $fullPath = "$path/$file"; + $splittedPath = explode("/src/$contextName/", $fullPath); - $classWithoutPrefix = str_replace(['.php', '/'], ['', '\\'], $splittedPath[1]); + $classWithoutPrefix = str_replace(['.php', '/'], ['', '\\'], $splittedPath[1]); - return "CodelyTv\\$contextName\\$classWithoutPrefix"; - }, - $files - ); + return "CodelyTv\\$contextName\\$classWithoutPrefix"; + }, + $files + ); - return array_merge($totalNamespaces, $namespaces); - }; - } + return array_merge($totalNamespaces, $namespaces); + }; + } } diff --git a/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php b/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php index d2e28a092..85f65ed63 100644 --- a/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php +++ b/src/Mooc/Shared/Infrastructure/Doctrine/DoctrinePrefixesSearcher.php @@ -10,43 +10,43 @@ final class DoctrinePrefixesSearcher { - private const MAPPINGS_PATH = 'Infrastructure/Persistence/Doctrine'; - - public static function inPath(string $path, string $baseNamespace): array - { - $possibleMappingDirectories = self::possibleMappingPaths($path); - $mappingDirectories = filter(self::isExistingMappingPath(), $possibleMappingDirectories); - - return array_flip(reindex(self::namespaceFormatter($baseNamespace), $mappingDirectories)); - } - - private static function modulesInPath(string $path): array - { - return filter( - static fn (string $possibleModule) => !in_array($possibleModule, ['.', '..'], true), - scandir($path) - ); - } - - private static function possibleMappingPaths(string $path): array - { - return map( - static function ($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) => !empty($path); - } - - private static function namespaceFormatter(string $baseNamespace): callable - { - return static fn (string $path, string $module) => "$baseNamespace\\$module\Domain"; - } + private const MAPPINGS_PATH = 'Infrastructure/Persistence/Doctrine'; + + public static function inPath(string $path, string $baseNamespace): array + { + $possibleMappingDirectories = self::possibleMappingPaths($path); + $mappingDirectories = filter(self::isExistingMappingPath(), $possibleMappingDirectories); + + return array_flip(reindex(self::namespaceFormatter($baseNamespace), $mappingDirectories)); + } + + private static function modulesInPath(string $path): array + { + return filter( + static fn (string $possibleModule): bool => !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 4153955e0..71f0b4566 100644 --- a/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php +++ b/src/Mooc/Shared/Infrastructure/Doctrine/MoocEntityManagerFactory.php @@ -9,25 +9,25 @@ final class MoocEntityManagerFactory { - private const SCHEMA_PATH = __DIR__ . '/../../../../../etc/databases/mooc.sql'; - - public static function create(array $parameters, string $environment): EntityManagerInterface - { - $isDevMode = 'prod' !== $environment; - - $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 - ); - } + 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/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 @@ +questions = $questions; + } +} diff --git a/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php b/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php new file mode 100644 index 000000000..157172596 --- /dev/null +++ b/src/Mooc/Steps/Domain/Quiz/QuizStepQuestion.php @@ -0,0 +1,22 @@ +question . '----' . implode('****', $this->answers); + } +} diff --git a/src/Mooc/Steps/Domain/Step.php b/src/Mooc/Steps/Domain/Step.php new file mode 100644 index 000000000..b59277cde --- /dev/null +++ b/src/Mooc/Steps/Domain/Step.php @@ -0,0 +1,16 @@ + + + + + + + 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/Videos/Application/Create/CreateVideoCommand.php b/src/Mooc/Videos/Application/Create/CreateVideoCommand.php index 636298100..0a94120f5 100644 --- a/src/Mooc/Videos/Application/Create/CreateVideoCommand.php +++ b/src/Mooc/Videos/Application/Create/CreateVideoCommand.php @@ -6,39 +6,38 @@ use CodelyTv\Shared\Domain\Bus\Command\Command; -final class CreateVideoCommand implements Command +final readonly class CreateVideoCommand implements Command { - public function __construct( - private readonly string $id, - private readonly string $type, - private readonly string $title, - private readonly string $url, - private readonly 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; - } + 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 e6b8b0d89..4e9222d0b 100644 --- a/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php +++ b/src/Mooc/Videos/Application/Create/CreateVideoCommandHandler.php @@ -11,20 +11,18 @@ use CodelyTv\Mooc\Videos\Domain\VideoType; use CodelyTv\Shared\Domain\Bus\Command\CommandHandler; -final class CreateVideoCommandHandler implements CommandHandler +final readonly class CreateVideoCommandHandler implements CommandHandler { - public function __construct(private readonly VideoCreator $creator) - { - } + public function __construct(private VideoCreator $creator) {} - public function __invoke(CreateVideoCommand $command): void - { - $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()); + 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()); - $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 91180306b..a3bb4a193 100644 --- a/src/Mooc/Videos/Application/Create/VideoCreator.php +++ b/src/Mooc/Videos/Application/Create/VideoCreator.php @@ -13,18 +13,16 @@ use CodelyTv\Mooc\Videos\Domain\VideoType; use CodelyTv\Shared\Domain\Bus\Event\EventBus; -final class VideoCreator +final readonly class VideoCreator { - public function __construct(private readonly VideoRepository $repository, private readonly EventBus $bus) - { - } + public function __construct(private VideoRepository $repository, private EventBus $bus) {} - 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->bus->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 eadef0af1..5df95d32b 100644 --- a/src/Mooc/Videos/Application/Find/FindVideoQuery.php +++ b/src/Mooc/Videos/Application/Find/FindVideoQuery.php @@ -6,14 +6,12 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; -final class FindVideoQuery implements Query +final readonly class FindVideoQuery implements Query { - public function __construct(private readonly string $id) - { - } + public function __construct(private string $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 4491614e5..c533ce272 100644 --- a/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php +++ b/src/Mooc/Videos/Application/Find/FindVideoQueryHandler.php @@ -6,22 +6,24 @@ use CodelyTv\Mooc\Videos\Domain\VideoId; use CodelyTv\Shared\Domain\Bus\Query\QueryHandler; + use function Lambdish\Phunctional\apply; -use function Lambdish\Phunctional\pipe; -final class FindVideoQueryHandler implements QueryHandler +final readonly class FindVideoQueryHandler implements QueryHandler { - private $finder; + private VideoResponseConverter $responseConverter; + + public function __construct(private VideoFinder $finder) + { + $this->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 6f99ef280..103ff6e75 100644 --- a/src/Mooc/Videos/Application/Find/VideoFinder.php +++ b/src/Mooc/Videos/Application/Find/VideoFinder.php @@ -4,21 +4,22 @@ namespace CodelyTv\Mooc\Videos\Application\Find; +use CodelyTv\Mooc\Videos\Domain\Video; use CodelyTv\Mooc\Videos\Domain\VideoFinder as DomainVideoFinder; use CodelyTv\Mooc\Videos\Domain\VideoId; use CodelyTv\Mooc\Videos\Domain\VideoRepository; final class VideoFinder { - private readonly DomainVideoFinder $finder; + private readonly DomainVideoFinder $finder; - public function __construct(VideoRepository $repository) - { - $this->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 6bd5fd336..fb1061754 100644 --- a/src/Mooc/Videos/Application/Find/VideoResponse.php +++ b/src/Mooc/Videos/Application/Find/VideoResponse.php @@ -6,14 +6,13 @@ use CodelyTv\Shared\Domain\Bus\Query\Response; -final class VideoResponse implements Response +final readonly class VideoResponse implements Response { - public function __construct( - private readonly string $id, - private readonly string $type, - private readonly string $title, - private readonly string $url, - private readonly string $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 6ee2b7b5f..c766fe0cf 100644 --- a/src/Mooc/Videos/Application/Find/VideoResponseConverter.php +++ b/src/Mooc/Videos/Application/Find/VideoResponseConverter.php @@ -8,14 +8,14 @@ final class VideoResponseConverter { - public function __invoke(Video $video): VideoResponse - { - return new VideoResponse( - $video->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 822916230..bb2ab5038 100644 --- a/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php +++ b/src/Mooc/Videos/Application/Trim/TrimVideoCommand.php @@ -6,24 +6,22 @@ use CodelyTv\Shared\Domain\Bus\Command\Command; -final class TrimVideoCommand implements Command +final readonly class TrimVideoCommand implements Command { - public function __construct(private readonly string $videoId, private readonly int $keepFromSecond, private readonly int $keepToSecond) - { - } + public function __construct(private string $videoId, private int $keepFromSecond, private int $keepToSecond) {} - public function videoId(): string - { - return $this->videoId; - } + public function videoId(): string + { + return $this->videoId; + } - public function keepFromSecond(): int - { - return $this->keepFromSecond; - } + public function keepFromSecond(): int + { + return $this->keepFromSecond; + } - public function keepToSecond(): int - { - return $this->keepToSecond; - } + 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 4f52d05fc..94cf1e676 100644 --- a/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php +++ b/src/Mooc/Videos/Application/Trim/TrimVideoCommandHandler.php @@ -7,17 +7,15 @@ use CodelyTv\Mooc\Videos\Domain\VideoId; use CodelyTv\Shared\Domain\SecondsInterval; -final class TrimVideoCommandHandler +final readonly class TrimVideoCommandHandler { - public function __construct(private readonly VideoTrimmer $trimmer) - { - } + public function __construct(private VideoTrimmer $trimmer) {} - public function __invoke(TrimVideoCommand $command): void - { - $id = new VideoId($command->videoId()); - $interval = SecondsInterval::fromValues($command->keepFromSecond(), $command->keepToSecond()); + public function __invoke(TrimVideoCommand $command): void + { + $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 d1415e4a9..75a6d4624 100644 --- a/src/Mooc/Videos/Application/Trim/VideoTrimmer.php +++ b/src/Mooc/Videos/Application/Trim/VideoTrimmer.php @@ -9,7 +9,5 @@ final class VideoTrimmer { - public function trim(VideoId $id, SecondsInterval $interval): void - { - } + public function trim(VideoId $id, SecondsInterval $interval): void {} } diff --git a/src/Mooc/Videos/Application/Update/VideoTitleUpdater.php b/src/Mooc/Videos/Application/Update/VideoTitleUpdater.php index cea6b77aa..523217fbd 100644 --- a/src/Mooc/Videos/Application/Update/VideoTitleUpdater.php +++ b/src/Mooc/Videos/Application/Update/VideoTitleUpdater.php @@ -9,21 +9,21 @@ use CodelyTv\Mooc\Videos\Domain\VideoRepository; use CodelyTv\Mooc\Videos\Domain\VideoTitle; -final class VideoTitleUpdater +final readonly class VideoTitleUpdater { - private readonly VideoFinder $finder; + private VideoFinder $finder; - public function __construct(private readonly VideoRepository $repository) - { - $this->finder = new VideoFinder($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 6028dc104..db7a35bdc 100644 --- a/src/Mooc/Videos/Domain/Video.php +++ b/src/Mooc/Videos/Domain/Video.php @@ -10,64 +10,57 @@ final class Video extends AggregateRoot { - public function __construct( - private readonly VideoId $id, - private readonly VideoType $type, - private VideoTitle $title, - private readonly VideoUrl $url, - private readonly CourseId $courseId - ) { - } + public function __construct( + private readonly VideoId $id, + private readonly VideoType $type, + private VideoTitle $title, + private readonly VideoUrl $url, + private readonly CourseId $courseId + ) {} - public static function create( - VideoId $id, - VideoType $type, - VideoTitle $title, - VideoUrl $url, - CourseId $courseId - ): Video { - $video = new self($id, $type, $title, $url, $courseId); + public static function create( + VideoId $id, + VideoType $type, + VideoTitle $title, + VideoUrl $url, + CourseId $courseId + ): self { + $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->value, $title->value(), $url->value(), $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 e820ac970..61cd7f8e8 100644 --- a/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php +++ b/src/Mooc/Videos/Domain/VideoCreatedDomainEvent.php @@ -8,47 +8,47 @@ final class VideoCreatedDomainEvent extends DomainEvent { - public function __construct( - string $id, - private readonly string $type, - private readonly string $title, - private readonly string $url, - private readonly string $courseId, - string $eventId = null, - string $occurredOn = null - ) { - parent::__construct($id, $eventId, $occurredOn); - } + public function __construct( + string $id, + private readonly string $type, + private readonly string $title, + private readonly string $url, + private readonly string $courseId, + string $eventId = null, + string $occurredOn = null + ) { + parent::__construct($id, $eventId, $occurredOn); + } - public static function eventName(): string - { - return 'video.created'; - } + public static function eventName(): string + { + return 'video.created'; + } - public static function fromPrimitives( - string $aggregateId, - array $body, - string $eventId, - string $occurredOn - ): VideoCreatedDomainEvent { - return new self( - $aggregateId, - $body['type'], - $body['title'], - $body['url'], - $body['course_id'], - $eventId, - $occurredOn - ); - } + 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, - ]; - } + 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 ba7a22f00..2386116ef 100644 --- a/src/Mooc/Videos/Domain/VideoFinder.php +++ b/src/Mooc/Videos/Domain/VideoFinder.php @@ -4,20 +4,18 @@ namespace CodelyTv\Mooc\Videos\Domain; -final class VideoFinder +final readonly class VideoFinder { - public function __construct(private readonly VideoRepository $repository) - { - } + public function __construct(private VideoRepository $repository) {} - public function __invoke(VideoId $id): Video - { - $video = $this->repository->search($id); + public function __invoke(VideoId $id): Video + { + $video = $this->repository->search($id); - if (null === $video) { - throw new VideoNotFound($id); - } + if ($video === null) { + throw new VideoNotFound($id); + } - return $video; - } + return $video; + } } diff --git a/src/Mooc/Videos/Domain/VideoId.php b/src/Mooc/Videos/Domain/VideoId.php index 2c8a3d177..4396d6b85 100644 --- a/src/Mooc/Videos/Domain/VideoId.php +++ b/src/Mooc/Videos/Domain/VideoId.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\Uuid; -final class VideoId extends Uuid -{ -} +final class VideoId extends Uuid {} diff --git a/src/Mooc/Videos/Domain/VideoNotFound.php b/src/Mooc/Videos/Domain/VideoNotFound.php index 7ba9f1dfd..fdbdf69de 100644 --- a/src/Mooc/Videos/Domain/VideoNotFound.php +++ b/src/Mooc/Videos/Domain/VideoNotFound.php @@ -8,18 +8,18 @@ final class VideoNotFound extends DomainError { - public function __construct(private readonly VideoId $id) - { - parent::__construct(); - } + public function __construct(private readonly VideoId $id) + { + parent::__construct(); + } - public function errorCode(): string - { - return 'video_not_found'; - } + public function errorCode(): string + { + return 'video_not_found'; + } - protected function errorMessage(): string - { - return sprintf('The video <%s> has not been found', $this->id->value()); - } + 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 7e68907a8..0cd6aae41 100644 --- a/src/Mooc/Videos/Domain/VideoRepository.php +++ b/src/Mooc/Videos/Domain/VideoRepository.php @@ -8,9 +8,9 @@ interface VideoRepository { - public function save(Video $video): void; + public function save(Video $video): void; - public function search(VideoId $id): ?Video; + public function search(VideoId $id): ?Video; - public function searchByCriteria(Criteria $criteria): Videos; + public function searchByCriteria(Criteria $criteria): Videos; } diff --git a/src/Mooc/Videos/Domain/VideoTitle.php b/src/Mooc/Videos/Domain/VideoTitle.php index 32e53aa88..d792cf453 100644 --- a/src/Mooc/Videos/Domain/VideoTitle.php +++ b/src/Mooc/Videos/Domain/VideoTitle.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class VideoTitle extends StringValueObject -{ -} +final class VideoTitle extends StringValueObject {} diff --git a/src/Mooc/Videos/Domain/VideoType.php b/src/Mooc/Videos/Domain/VideoType.php index 439c785b1..13a3a23f4 100644 --- a/src/Mooc/Videos/Domain/VideoType.php +++ b/src/Mooc/Videos/Domain/VideoType.php @@ -4,20 +4,8 @@ namespace CodelyTv\Mooc\Videos\Domain; -use CodelyTv\Shared\Domain\ValueObject\Enum; -use InvalidArgumentException; - -/** - * @method static VideoType screencast() - * @method static VideoType interview() - */ -final class VideoType extends Enum +enum VideoType: string { - public const SCREENCAST = 'screencast'; - public const INTERVIEW = 'interview'; - - protected function throwExceptionForInvalidValue($value): never - { - throw new InvalidArgumentException(sprintf('The <%s> 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 e749b0726..9a21231d5 100644 --- a/src/Mooc/Videos/Domain/Videos.php +++ b/src/Mooc/Videos/Domain/Videos.php @@ -8,8 +8,8 @@ final class Videos extends Collection { - protected function type(): string - { - return Video::class; - } + protected function type(): string + { + return Video::class; + } } diff --git a/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php index 7f5eef170..2db8f8d8c 100644 --- a/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php +++ b/src/Mooc/Videos/Infrastructure/Persistence/Doctrine/VideoIdType.php @@ -9,8 +9,8 @@ final class VideoIdType extends UuidType { - protected function typeClassName(): string - { - return VideoId::class; - } + protected function typeClassName(): string + { + return VideoId::class; + } } diff --git a/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php b/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php index 933277438..76e4d8a0c 100644 --- a/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php +++ b/src/Mooc/Videos/Infrastructure/Persistence/VideoRepositoryMySql.php @@ -14,29 +14,29 @@ final class VideoRepositoryMySql extends DoctrineRepository implements VideoRepository { - 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); - } + 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/Shared/Domain/Aggregate/AggregateRoot.php b/src/Shared/Domain/Aggregate/AggregateRoot.php index 80fa7ddbd..8162916cf 100644 --- a/src/Shared/Domain/Aggregate/AggregateRoot.php +++ b/src/Shared/Domain/Aggregate/AggregateRoot.php @@ -8,18 +8,18 @@ abstract class AggregateRoot { - private array $domainEvents = []; + private array $domainEvents = []; - final public function pullDomainEvents(): array - { - $domainEvents = $this->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/Assert.php b/src/Shared/Domain/Assert.php index d47b45939..d1c5f6e2d 100644 --- a/src/Shared/Domain/Assert.php +++ b/src/Shared/Domain/Assert.php @@ -8,19 +8,17 @@ final class Assert { - public static function arrayOf(string $class, array $items): void - { - foreach ($items as $item) { - self::instanceOf($class, $item); - } - } + public static function arrayOf(string $class, array $items): void + { + foreach ($items as $item) { + self::instanceOf($class, $item); + } + } - public static function instanceOf(string $class, $item): void - { - if (!$item instanceof $class) { - throw new InvalidArgumentException( - sprintf('The object <%s> is not an instance of <%s>', $class, $item::class) - ); - } - } + 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 d8343b898..dceac12c7 100644 --- a/src/Shared/Domain/Bus/Command/Command.php +++ b/src/Shared/Domain/Bus/Command/Command.php @@ -4,6 +4,4 @@ namespace CodelyTv\Shared\Domain\Bus\Command; -interface Command -{ -} +interface Command {} diff --git a/src/Shared/Domain/Bus/Command/CommandBus.php b/src/Shared/Domain/Bus/Command/CommandBus.php index 2c82ef796..fdf5e9ef9 100644 --- a/src/Shared/Domain/Bus/Command/CommandBus.php +++ b/src/Shared/Domain/Bus/Command/CommandBus.php @@ -6,5 +6,5 @@ interface CommandBus { - public function dispatch(Command $command): void; + public function dispatch(Command $command): void; } diff --git a/src/Shared/Domain/Bus/Command/CommandHandler.php b/src/Shared/Domain/Bus/Command/CommandHandler.php index 2cea0d72d..1168c1df4 100644 --- a/src/Shared/Domain/Bus/Command/CommandHandler.php +++ b/src/Shared/Domain/Bus/Command/CommandHandler.php @@ -4,6 +4,4 @@ namespace CodelyTv\Shared\Domain\Bus\Command; -interface CommandHandler -{ -} +interface CommandHandler {} diff --git a/src/Shared/Domain/Bus/Event/DomainEvent.php b/src/Shared/Domain/Bus/Event/DomainEvent.php index 0bca368d4..ffc7b0641 100644 --- a/src/Shared/Domain/Bus/Event/DomainEvent.php +++ b/src/Shared/Domain/Bus/Event/DomainEvent.php @@ -5,43 +5,43 @@ namespace CodelyTv\Shared\Domain\Bus\Event; use CodelyTv\Shared\Domain\Utils; -use CodelyTv\Shared\Domain\ValueObject\Uuid; +use CodelyTv\Shared\Domain\ValueObject\SimpleUuid; use DateTimeImmutable; abstract class DomainEvent { - private readonly string $eventId; - private readonly string $occurredOn; - - public function __construct(private readonly string $aggregateId, string $eventId = null, string $occurredOn = null) - { - $this->eventId = $eventId ?: Uuid::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; - - public function aggregateId(): string - { - return $this->aggregateId; - } - - public function eventId(): string - { - return $this->eventId; - } - - public function occurredOn(): string - { - return $this->occurredOn; - } + 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/DomainEventSubscriber.php b/src/Shared/Domain/Bus/Event/DomainEventSubscriber.php index 54903808c..a94a22bc9 100644 --- a/src/Shared/Domain/Bus/Event/DomainEventSubscriber.php +++ b/src/Shared/Domain/Bus/Event/DomainEventSubscriber.php @@ -6,5 +6,5 @@ interface DomainEventSubscriber { - public static function subscribedTo(): array; + public static function subscribedTo(): array; } diff --git a/src/Shared/Domain/Bus/Event/EventBus.php b/src/Shared/Domain/Bus/Event/EventBus.php index 164f77e9b..3be5d9782 100644 --- a/src/Shared/Domain/Bus/Event/EventBus.php +++ b/src/Shared/Domain/Bus/Event/EventBus.php @@ -6,5 +6,5 @@ interface EventBus { - public function publish(DomainEvent ...$events): void; + public function publish(DomainEvent ...$events): void; } diff --git a/src/Shared/Domain/Bus/Query/Query.php b/src/Shared/Domain/Bus/Query/Query.php index 319bac80b..3ed4d1467 100644 --- a/src/Shared/Domain/Bus/Query/Query.php +++ b/src/Shared/Domain/Bus/Query/Query.php @@ -4,6 +4,4 @@ namespace CodelyTv\Shared\Domain\Bus\Query; -interface Query -{ -} +interface Query {} diff --git a/src/Shared/Domain/Bus/Query/QueryBus.php b/src/Shared/Domain/Bus/Query/QueryBus.php index dc74a4b9b..8abee3634 100644 --- a/src/Shared/Domain/Bus/Query/QueryBus.php +++ b/src/Shared/Domain/Bus/Query/QueryBus.php @@ -6,5 +6,5 @@ interface QueryBus { - public function ask(Query $query): ?Response; + public function ask(Query $query): ?Response; } diff --git a/src/Shared/Domain/Bus/Query/QueryHandler.php b/src/Shared/Domain/Bus/Query/QueryHandler.php index dc4a36392..f849e7210 100644 --- a/src/Shared/Domain/Bus/Query/QueryHandler.php +++ b/src/Shared/Domain/Bus/Query/QueryHandler.php @@ -4,6 +4,4 @@ namespace CodelyTv\Shared\Domain\Bus\Query; -interface QueryHandler -{ -} +interface QueryHandler {} diff --git a/src/Shared/Domain/Bus/Query/Response.php b/src/Shared/Domain/Bus/Query/Response.php index 3bf921ee0..dd8be87cb 100644 --- a/src/Shared/Domain/Bus/Query/Response.php +++ b/src/Shared/Domain/Bus/Query/Response.php @@ -4,6 +4,4 @@ namespace CodelyTv\Shared\Domain\Bus\Query; -interface Response -{ -} +interface Response {} diff --git a/src/Shared/Domain/Collection.php b/src/Shared/Domain/Collection.php index 137e6c234..c0b664aa8 100644 --- a/src/Shared/Domain/Collection.php +++ b/src/Shared/Domain/Collection.php @@ -7,28 +7,30 @@ use ArrayIterator; use Countable; use IteratorAggregate; +use Traversable; +/** @template-implements IteratorAggregate*/ abstract class Collection implements Countable, IteratorAggregate { - public function __construct(private readonly array $items) - { - Assert::arrayOf($this->type(), $items); - } - - abstract protected function type(): string; - - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->items()); - } - - public function count(): int - { - return count($this->items()); - } - - protected function items(): array - { - 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 1c762ed23..876c9f441 100644 --- a/src/Shared/Domain/Criteria/Criteria.php +++ b/src/Shared/Domain/Criteria/Criteria.php @@ -4,59 +4,58 @@ namespace CodelyTv\Shared\Domain\Criteria; -final class Criteria +final readonly class Criteria { - public function __construct( - private readonly Filters $filters, - private readonly Order $order, - private readonly ?int $offset, - private readonly ?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, - $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 5a8cbf3bf..33f15c365 100644 --- a/src/Shared/Domain/Criteria/Filter.php +++ b/src/Shared/Domain/Criteria/Filter.php @@ -4,41 +4,40 @@ namespace CodelyTv\Shared\Domain\Criteria; -final class Filter +final readonly class Filter { - public function __construct( - private readonly FilterField $field, - private readonly FilterOperator $operator, - private readonly FilterValue $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 serialize(): string - { - return sprintf('%s.%s.%s', $this->field->value(), $this->operator->value(), $this->value->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 7c952bbb5..fcc11e2de 100644 --- a/src/Shared/Domain/Criteria/FilterField.php +++ b/src/Shared/Domain/Criteria/FilterField.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class FilterField extends StringValueObject -{ -} +final class FilterField extends StringValueObject {} diff --git a/src/Shared/Domain/Criteria/FilterOperator.php b/src/Shared/Domain/Criteria/FilterOperator.php index 1c9c3b8ff..3bd946967 100644 --- a/src/Shared/Domain/Criteria/FilterOperator.php +++ b/src/Shared/Domain/Criteria/FilterOperator.php @@ -4,31 +4,17 @@ namespace CodelyTv\Shared\Domain\Criteria; -use CodelyTv\Shared\Domain\ValueObject\Enum; -use InvalidArgumentException; - -/** - * @method static FilterOperator gt() - * @method static FilterOperator lt() - * @method static FilterOperator like() - */ -final class FilterOperator extends Enum +enum FilterOperator: string { - public const EQUAL = '='; - public const NOT_EQUAL = '!='; - public const GT = '>'; - public const LT = '<'; - public const CONTAINS = 'CONTAINS'; - public const NOT_CONTAINS = 'NOT_CONTAINS'; - private static array $containing = [self::CONTAINS, self::NOT_CONTAINS]; - - public function isContaining(): bool - { - return in_array($this->value(), self::$containing, true); - } + case EQUAL = '='; + case NOT_EQUAL = '!='; + case GT = '>'; + case LT = '<'; + case CONTAINS = 'CONTAINS'; + case NOT_CONTAINS = 'NOT_CONTAINS'; - protected function throwExceptionForInvalidValue($value): never - { - 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 08151b09e..5576c10fb 100644 --- a/src/Shared/Domain/Criteria/FilterValue.php +++ b/src/Shared/Domain/Criteria/FilterValue.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class FilterValue extends StringValueObject -{ -} +final class FilterValue extends StringValueObject {} diff --git a/src/Shared/Domain/Criteria/Filters.php b/src/Shared/Domain/Criteria/Filters.php index b55a5e0f2..bea6863e3 100644 --- a/src/Shared/Domain/Criteria/Filters.php +++ b/src/Shared/Domain/Criteria/Filters.php @@ -5,41 +5,42 @@ namespace CodelyTv\Shared\Domain\Criteria; use CodelyTv\Shared\Domain\Collection; + use function Lambdish\Phunctional\reduce; final class Filters extends Collection { - 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::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) => sprintf('%s^%s', $accumulate, $filter->serialize()), - $this->items(), - '' - ); - } - - protected function type(): string - { - return Filter::class; - } + 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 643a65268..dfbc8e016 100644 --- a/src/Shared/Domain/Criteria/Order.php +++ b/src/Shared/Domain/Criteria/Order.php @@ -4,44 +4,45 @@ namespace CodelyTv\Shared\Domain\Criteria; -final class Order +final readonly class Order { - public function __construct(private readonly OrderBy $orderBy, private readonly OrderType $orderType) - { - } - - public static function createDesc(OrderBy $orderBy): Order - { - return new self($orderBy, OrderType::desc()); - } - - public static function fromValues(?string $orderBy, ?string $order): Order - { - return null === $orderBy ? self::none() : new Order(new OrderBy($orderBy), new OrderType($order)); - } - - public static function none(): Order - { - return new Order(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()); - } + 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 c6e43e6b9..d2d054cae 100644 --- a/src/Shared/Domain/Criteria/OrderBy.php +++ b/src/Shared/Domain/Criteria/OrderBy.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\StringValueObject; -final class OrderBy extends StringValueObject -{ -} +final class OrderBy extends StringValueObject {} diff --git a/src/Shared/Domain/Criteria/OrderType.php b/src/Shared/Domain/Criteria/OrderType.php index 2ffc46071..851bc0c7f 100644 --- a/src/Shared/Domain/Criteria/OrderType.php +++ b/src/Shared/Domain/Criteria/OrderType.php @@ -4,27 +4,14 @@ namespace CodelyTv\Shared\Domain\Criteria; -use CodelyTv\Shared\Domain\ValueObject\Enum; -use InvalidArgumentException; - -/** - * @method static OrderType asc() - * @method static OrderType desc() - * @method static OrderType none() - */ -final class OrderType extends Enum +enum OrderType: string { - public const ASC = 'asc'; - public const DESC = 'desc'; - public const NONE = 'none'; - - public function isNone(): bool - { - return $this->equals(self::none()); - } + case ASC = 'asc'; + case DESC = 'desc'; + case NONE = 'none'; - protected function throwExceptionForInvalidValue($value): never - { - throw new InvalidArgumentException($value); - } + public function isNone(): bool + { + return $this->value === self::NONE->value; + } } diff --git a/src/Shared/Domain/DomainError.php b/src/Shared/Domain/DomainError.php index 8ecd4725f..18eec3f93 100644 --- a/src/Shared/Domain/DomainError.php +++ b/src/Shared/Domain/DomainError.php @@ -8,12 +8,12 @@ abstract class DomainError extends DomainException { - public function __construct() - { - parent::__construct($this->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/Logger.php b/src/Shared/Domain/Logger.php index 37100e698..fbaa7710c 100644 --- a/src/Shared/Domain/Logger.php +++ b/src/Shared/Domain/Logger.php @@ -6,9 +6,9 @@ interface Logger { - public function info(string $message, array $context = []): void; + public function info(string $message, array $context = []): void; - public function warning(string $message, array $context = []): void; + public function warning(string $message, array $context = []): void; - public function critical(string $message, array $context = []): void; + public function critical(string $message, array $context = []): void; } diff --git a/src/Shared/Domain/Monitoring.php b/src/Shared/Domain/Monitoring.php index cb831294e..2602a0f40 100644 --- a/src/Shared/Domain/Monitoring.php +++ b/src/Shared/Domain/Monitoring.php @@ -6,13 +6,13 @@ interface Monitoring { - public function incrementCounter(int $times): void; + public function incrementCounter(int $times): void; - public function incrementGauge(int $times): void; + public function incrementGauge(int $times): void; - public function decrementGauge(int $times): void; + public function decrementGauge(int $times): void; - public function setGauge(int $value): void; + public function setGauge(int $value): void; - public function observeHistogram(int $value, array $labels = []): void; + public function observeHistogram(int $value, array $labels = []): void; } diff --git a/src/Shared/Domain/RandomNumberGenerator.php b/src/Shared/Domain/RandomNumberGenerator.php index fb8462781..6fe49b715 100644 --- a/src/Shared/Domain/RandomNumberGenerator.php +++ b/src/Shared/Domain/RandomNumberGenerator.php @@ -6,5 +6,5 @@ interface RandomNumberGenerator { - public function generate(): int; + public function generate(): int; } diff --git a/src/Shared/Domain/Second.php b/src/Shared/Domain/Second.php index 6ed51277e..6ff9f4bcb 100644 --- a/src/Shared/Domain/Second.php +++ b/src/Shared/Domain/Second.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\ValueObject\IntValueObject; -final class Second extends IntValueObject -{ -} +final class Second extends IntValueObject {} diff --git a/src/Shared/Domain/SecondsInterval.php b/src/Shared/Domain/SecondsInterval.php index 55df80a10..9fdffeccf 100644 --- a/src/Shared/Domain/SecondsInterval.php +++ b/src/Shared/Domain/SecondsInterval.php @@ -6,22 +6,22 @@ use DomainException; -final class SecondsInterval +final readonly class SecondsInterval { - public function __construct(private readonly Second $from, private readonly Second $to) - { - $this->ensureIntervalEndsAfterStart($from, $to); - } + public function __construct(private Second $from, private Second $to) + { + $this->ensureIntervalEndsAfterStart($from, $to); + } - public static function fromValues(int $from, int $to): SecondsInterval - { - return new self(new Second($from), new Second($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'); - } - } + 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 index e0eb706f8..92a9d1117 100644 --- a/src/Shared/Domain/Utils.php +++ b/src/Shared/Domain/Utils.php @@ -6,93 +6,68 @@ use DateTimeImmutable; use DateTimeInterface; -use ReflectionClass; -use RuntimeException; use function Lambdish\Phunctional\filter; final class Utils { - public static function endsWith(string $needle, string $haystack): bool - { - $length = strlen($needle); - if ($length === 0) { - return true; - } - - return (substr($haystack, -$length) === $needle); - } - - public static function dateToString(DateTimeInterface $date): string - { - return $date->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 - { - $data = json_decode($json, true); - - if (JSON_ERROR_NONE !== json_last_error()) { - throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); - } - - return $data; - } - - 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, static::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) => strstr($possibleModule, $fileType), - scandir($path) - ); - } - - public static function extractClassName(object $object): string - { - $reflect = new ReflectionClass($object); - - return $reflect->getShortName(); - } - - public static function iterableToArray(iterable $iterable): array - { - if (is_array($iterable)) { - return $iterable; - } - - return iterator_to_array($iterable); - } + public static function dateToString(DateTimeInterface $date): string + { + return $date->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 index 313b7c249..1064a6edc 100644 --- a/src/Shared/Domain/UuidGenerator.php +++ b/src/Shared/Domain/UuidGenerator.php @@ -6,5 +6,5 @@ interface UuidGenerator { - public function generate(): string; + public function generate(): string; } diff --git a/src/Shared/Domain/ValueObject/Enum.php b/src/Shared/Domain/ValueObject/Enum.php deleted file mode 100644 index 1d7eed987..000000000 --- a/src/Shared/Domain/ValueObject/Enum.php +++ /dev/null @@ -1,82 +0,0 @@ -ensureIsBetweenAcceptedValues($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): Enum - { - 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 static function random(): static - { - return new static(self::randomValue()); - } - - private static function keysFormatter(): callable - { - return static fn ($unused, string $key): string => Utils::toCamelCase(strtolower($key)); - } - - public function value() - { - return $this->value; - } - - public function equals(Enum $other): bool - { - return $other == $this; - } - - public function __toString(): string - { - return (string) $this->value(); - } - - private function ensureIsBetweenAcceptedValues($value): void - { - if (!in_array($value, static::values(), true)) { - $this->throwExceptionForInvalidValue($value); - } - } -} diff --git a/src/Shared/Domain/ValueObject/IntValueObject.php b/src/Shared/Domain/ValueObject/IntValueObject.php index e75774629..77e13cd76 100644 --- a/src/Shared/Domain/ValueObject/IntValueObject.php +++ b/src/Shared/Domain/ValueObject/IntValueObject.php @@ -6,17 +6,15 @@ abstract class IntValueObject { - public function __construct(protected int $value) - { - } + public function __construct(protected int $value) {} - public function value(): int - { - return $this->value; - } + final public function value(): int + { + return $this->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; - } + 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 7bf9e9c00..63a30fcc8 100644 --- a/src/Shared/Domain/ValueObject/Uuid.php +++ b/src/Shared/Domain/ValueObject/Uuid.php @@ -8,37 +8,37 @@ use Ramsey\Uuid\Uuid as RamseyUuid; use Stringable; -class Uuid implements Stringable +abstract class Uuid implements Stringable { - public function __construct(protected string $value) - { - $this->ensureIsValidUuid($value); - } - - public static function random(): self - { - return new static(RamseyUuid::uuid4()->toString()); - } - - public function value(): string - { - return $this->value; - } - - public function equals(Uuid $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>.', static::class, $id)); - } - } + 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/Bus/CallableFirstParameterExtractor.php b/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php index eb5cb6efe..23564a9e1 100644 --- a/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php +++ b/src/Shared/Infrastructure/Bus/CallableFirstParameterExtractor.php @@ -9,71 +9,72 @@ use ReflectionClass; use ReflectionMethod; use ReflectionNamedType; + use function Lambdish\Phunctional\map; use function Lambdish\Phunctional\reduce; use function Lambdish\Phunctional\reindex; final class CallableFirstParameterExtractor { - 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 static function classExtractor(CallableFirstParameterExtractor $parameterExtractor): callable - { - return static fn (callable $handler): ?string => $parameterExtractor->extract($handler); - } - - private static function pipedCallablesReducer(): callable - { - return static function ($subscribers, DomainEventSubscriber $subscriber): array { - $subscribedEvents = $subscriber::subscribedTo(); - - foreach ($subscribedEvents as $subscribedEvent) { - $subscribers[$subscribedEvent][] = $subscriber; - } - - return $subscribers; - }; - } - - private static function unflatten(): callable - { - return static fn ($value) => [$value]; - } - - public function extract($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 $fistParameterType */ - $fistParameterType = $method->getParameters()[0]->getType(); - - if (null === $fistParameterType) { - 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; - } + 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 static function classExtractor(self $parameterExtractor): callable + { + return static fn (object $handler): ?string => $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/CommandNotRegisteredError.php b/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php index 90efecda7..fdb18e189 100644 --- a/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php +++ b/src/Shared/Infrastructure/Bus/Command/CommandNotRegisteredError.php @@ -9,10 +9,10 @@ final class CommandNotRegisteredError extends RuntimeException { - public function __construct(Command $command) - { - $commandClass = $command::class; + public function __construct(Command $command) + { + $commandClass = $command::class; - parent::__construct("The command <$commandClass> hasn't a command handler associated"); - } + 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 index ab94755cc..4f6bbed0e 100644 --- a/src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php +++ b/src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php @@ -15,27 +15,27 @@ final class InMemorySymfonyCommandBus implements CommandBus { - private readonly MessageBus $bus; + private readonly MessageBus $bus; - public function __construct(iterable $commandHandlers) - { - $this->bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator(CallableFirstParameterExtractor::forCallables($commandHandlers)) - ), - ] - ); - } + public function __construct(iterable $commandHandlers) + { + $this->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; - } - } + 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/Event/DomainEventJsonDeserializer.php b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php index cb2e77213..3d73fa61b 100644 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonDeserializer.php @@ -6,29 +6,22 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEvent; use CodelyTv\Shared\Domain\Utils; -use RuntimeException; -final class DomainEventJsonDeserializer +final readonly class DomainEventJsonDeserializer { - public function __construct(private readonly DomainEventMapping $mapping) - { - } + public function __construct(private DomainEventMapping $mapping) {} - public function deserialize(string $domainEvent): DomainEvent - { - $eventData = Utils::jsonDecode($domainEvent); - $eventName = $eventData['data']['type']; - $eventClass = $this->mapping->for($eventName); + public function deserialize(string $domainEvent): DomainEvent + { + $eventData = Utils::jsonDecode($domainEvent); + $eventName = $eventData['data']['type']; + $eventClass = $this->mapping->for($eventName); - if (null === $eventClass) { - throw new RuntimeException("The event <$eventName> doesn't exist or has no subscribers"); - } - - return $eventClass::fromPrimitives( - $eventData['data']['attributes']['id'], - $eventData['data']['attributes'], - $eventData['data']['id'], - $eventData['data']['occurred_on'] - ); - } + 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 index 011a1ff86..293e5f996 100644 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventJsonSerializer.php +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventJsonSerializer.php @@ -8,18 +8,18 @@ final class DomainEventJsonSerializer { - public static function serialize(DomainEvent $domainEvent): string - { - return json_encode( - [ - 'data' => [ - 'id' => $domainEvent->eventId(), - 'type' => $domainEvent::eventName(), - 'occurred_on' => $domainEvent->occurredOn(), - 'attributes' => array_merge($domainEvent->toPrimitives(), ['id' => $domainEvent->aggregateId()]), - ], - 'meta' => [], - ] - ); - } + public static function serialize(DomainEvent $domainEvent): string + { + return json_encode( + [ + 'data' => [ + '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 956d864bc..697acad8d 100644 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventMapping.php @@ -6,40 +6,38 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; use RuntimeException; + use function Lambdish\Phunctional\reduce; use function Lambdish\Phunctional\reindex; final class DomainEventMapping { - private $mapping; - - public function __construct(iterable $mapping) - { - $this->mapping = reduce($this->eventsExtractor(), $mapping, []); - } - - public function for(string $name) - { - 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_merge( - $mapping, - reindex( - $this->eventNameExtractor(), - $subscriber::subscribedTo() - ) - ); - } - - private function eventNameExtractor(): callable - { - return static fn (string $eventClass): string => $eventClass::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/DomainEventSubscriberLocator.php b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php index f456bc573..d39aa7651 100644 --- a/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php +++ b/src/Shared/Infrastructure/Bus/Event/DomainEventSubscriberLocator.php @@ -9,41 +9,42 @@ use CodelyTv\Shared\Infrastructure\Bus\Event\RabbitMq\RabbitMqQueueNameFormatter; use RuntimeException; use Traversable; + use function Lambdish\Phunctional\search; final class DomainEventSubscriberLocator { - private readonly array $mapping; - - public function __construct(Traversable $mapping) - { - $this->mapping = iterator_to_array($mapping); - } - - public function allSubscribedTo(string $eventClass): array - { - $formatted = CallableFirstParameterExtractor::forPipedCallables($this->mapping); - - return $formatted[$eventClass]; - } - - public function withRabbitMqQueueNamed(string $queueName): DomainEventSubscriber|callable - { - $subscriber = search( - static fn (DomainEventSubscriber $subscriber) => RabbitMqQueueNameFormatter::format($subscriber) === - $queueName, - $this->mapping - ); - - if (null === $subscriber) { - throw new RuntimeException("There are no subscribers for the <$queueName> queue"); - } - - return $subscriber; - } - - public function all(): array - { - return $this->mapping; - } + private readonly array $mapping; + + public function __construct(Traversable $mapping) + { + $this->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/InMemory/InMemorySymfonyEventBus.php b/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php index d894ad23a..db19cb695 100644 --- a/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php +++ b/src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php @@ -14,28 +14,26 @@ class InMemorySymfonyEventBus implements EventBus { - private readonly MessageBus $bus; + private readonly MessageBus $bus; - public function __construct(iterable $subscribers) - { - $this->bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator( - CallableFirstParameterExtractor::forPipedCallables($subscribers) - ) - ), - ] - ); - } + public function __construct(iterable $subscribers) + { + $this->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) { - } - } - } + 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 index 85b3481cc..a07e3b09f 100644 --- a/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineDomainEventsConsumer.php +++ b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineDomainEventsConsumer.php @@ -8,61 +8,61 @@ use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventMapping; use DateTimeImmutable; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; use Doctrine\ORM\EntityManager; use RuntimeException; + use function Lambdish\Phunctional\each; use function Lambdish\Phunctional\map; -final class MySqlDoctrineDomainEventsConsumer +final readonly class MySqlDoctrineDomainEventsConsumer { - private readonly Connection $connection; + private Connection $connection; - public function __construct(EntityManager $entityManager, private readonly DomainEventMapping $eventMapping) - { - $this->connection = $entityManager->getConnection(); - } + public function __construct(EntityManager $entityManager, private DomainEventMapping $eventMapping) + { + $this->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") - ->fetchAll(FetchMode::ASSOCIATIVE); + 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); + each($this->executeSubscribers($subscribers), $events); - $ids = implode(', ', map($this->idExtractor(), $events)); + $ids = implode(', ', map($this->idExtractor(), $events)); - if (!empty($ids)) { - $this->connection->executeUpdate("DELETE FROM domain_events WHERE id IN ($ids)"); - } - } + 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']) - ); + 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) { - } - }; - } + $subscribers($domainEvent); + } catch (RuntimeException) { + } + }; + } - private function formatDate($stringDate): string - { - return Utils::dateToString(new DateTimeImmutable($stringDate)); - } + private function formatDate(mixed $stringDate): string + { + return Utils::dateToString(new DateTimeImmutable($stringDate)); + } - private function idExtractor(): callable - { - return static fn (array $event): string => "'${event['id']}'"; - } + 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 index 77c8e451c..2b21c3897 100644 --- a/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php +++ b/src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php @@ -9,40 +9,41 @@ use CodelyTv\Shared\Domain\Utils; use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManager; + use function Lambdish\Phunctional\each; final class MySqlDoctrineEventBus implements EventBus { - private const DATABASE_TIMESTAMP_FORMAT = 'Y-m-d H:i:s'; - private readonly Connection $connection; - - public function __construct(EntityManager $entityManager) - { - $this->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->executeUpdate( - <<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 (null !== $deadLetterExchange) { - $queue->setArgument('x-dead-letter-exchange', $deadLetterExchange); - } - - if (null !== $deadLetterRoutingKey) { - $queue->setArgument('x-dead-letter-routing-key', $deadLetterRoutingKey); - } - - if (null !== $messageTtl) { - $queue->setArgument('x-message-ttl', $messageTtl); - } - - $queue->setFlags(AMQP_DURABLE); - $queue->declareQueue(); - - return $queue; - } + public function __construct(private RabbitMqConnection $connection) {} + + public function configure(string $exchangeName, DomainEventSubscriber ...$subscribers): void + { + $retryExchangeName = RabbitMqExchangeNameFormatter::retry($exchangeName); + $deadLetterExchangeName = RabbitMqExchangeNameFormatter::deadLetter($exchangeName); + + $this->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 index a43fa35d8..ea3bcb9fd 100644 --- a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqConnection.php +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqConnection.php @@ -11,60 +11,58 @@ final class RabbitMqConnection { - private static ?AMQPConnection $connection = null; - private static ?AMQPChannel $channel = null; - /** @var AMQPExchange[] */ - private static array $exchanges = []; - /** @var AMQPQueue[] */ - private static array $queues = []; - - public function __construct(private readonly array $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 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 (null === self::$connection) { - self::$connection = new AMQPConnection($this->configuration); - } - - if (!self::$connection->isConnected()) { - self::$connection->pconnect(); - } - - return self::$connection; - } + private static ?AMQPConnection $connection = null; + private static ?AMQPChannel $channel = null; + /** @var AMQPExchange[] */ + private static array $exchanges = []; + /** @var AMQPQueue[] */ + private static array $queues = []; + + public function __construct(private readonly array $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 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 index 8c007002b..ea18ec7ce 100644 --- a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqDomainEventsConsumer.php +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqDomainEventsConsumer.php @@ -7,86 +7,87 @@ use AMQPEnvelope; use AMQPQueue; use AMQPQueueException; +use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventJsonDeserializer; use Throwable; + use function Lambdish\Phunctional\assoc; use function Lambdish\Phunctional\get; -final class RabbitMqDomainEventsConsumer +final readonly class RabbitMqDomainEventsConsumer { - public function __construct( - private readonly RabbitMqConnection $connection, - private readonly DomainEventJsonDeserializer $deserializer, - private readonly string $exchangeName, - private readonly int $maxRetries - ) { - } - - public function consume(callable $subscriber, string $queueName): void - { - try { - $this->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), - ] - ); - } + public function __construct( + private RabbitMqConnection $connection, + private DomainEventJsonDeserializer $deserializer, + private string $exchangeName, + private int $maxRetries + ) {} + + public function consume(callable | DomainEventSubscriber $subscriber, string $queueName): void + { + try { + $this->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 index 2440afda0..1d7bc1a89 100644 --- a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php @@ -9,48 +9,48 @@ use CodelyTv\Shared\Domain\Bus\Event\EventBus; use CodelyTv\Shared\Infrastructure\Bus\Event\DomainEventJsonSerializer; use CodelyTv\Shared\Infrastructure\Bus\Event\MySql\MySqlDoctrineEventBus; + use function Lambdish\Phunctional\each; -final class RabbitMqEventBus implements EventBus +final readonly class RabbitMqEventBus implements EventBus { - public function __construct( - private readonly RabbitMqConnection $connection, - private readonly string $exchangeName, - private readonly MySqlDoctrineEventBus $failoverPublisher - ) { - } - - public function publish(DomainEvent ...$events): void - { - each($this->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', - ] - ); - } + public function __construct( + private RabbitMqConnection $connection, + private string $exchangeName, + private MySqlDoctrineEventBus $failoverPublisher + ) {} + + public function publish(DomainEvent ...$events): void + { + each($this->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 index db78767b7..7e9be1dc9 100644 --- a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqExchangeNameFormatter.php +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqExchangeNameFormatter.php @@ -6,13 +6,13 @@ final class RabbitMqExchangeNameFormatter { - public static function retry(string $exchangeName): string - { - return "retry-$exchangeName"; - } + public static function retry(string $exchangeName): string + { + return "retry-$exchangeName"; + } - public static function deadLetter(string $exchangeName): string - { - return "dead_letter-$exchangeName"; - } + public static function deadLetter(string $exchangeName): string + { + return "dead_letter-$exchangeName"; + } } diff --git a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqQueueNameFormatter.php b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqQueueNameFormatter.php index 15bf09e9a..052380602 100644 --- a/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqQueueNameFormatter.php +++ b/src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqQueueNameFormatter.php @@ -6,48 +6,49 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; use CodelyTv\Shared\Domain\Utils; + use function Lambdish\Phunctional\last; use function Lambdish\Phunctional\map; final class RabbitMqQueueNameFormatter { - public static function format(DomainEventSubscriber $subscriber): string - { - $subscriberClassPaths = explode('\\', str_replace('CodelyTv', 'codelytv', $subscriber::class)); + public static function format(DomainEventSubscriber $subscriber): string + { + $subscriberClassPaths = explode('\\', str_replace('CodelyTv', 'codelytv', $subscriber::class)); - $queueNameParts = [ - $subscriberClassPaths[0], - $subscriberClassPaths[1], - $subscriberClassPaths[2], - last($subscriberClassPaths), - ]; + $queueNameParts = [ + $subscriberClassPaths[0], + $subscriberClassPaths[1], + $subscriberClassPaths[2], + last($subscriberClassPaths), + ]; - return implode('.', map(self::toSnakeCase(), $queueNameParts)); - } + return implode('.', map(self::toSnakeCase(), $queueNameParts)); + } - public static function formatRetry(DomainEventSubscriber $subscriber): string - { - $queueName = self::format($subscriber); + public static function formatRetry(DomainEventSubscriber $subscriber): string + { + $queueName = self::format($subscriber); - return "retry.$queueName"; - } + return "retry.$queueName"; + } - public static function formatDeadLetter(DomainEventSubscriber $subscriber): string - { - $queueName = self::format($subscriber); + public static function formatDeadLetter(DomainEventSubscriber $subscriber): string + { + $queueName = self::format($subscriber); - return "dead_letter.$queueName"; - } + return "dead_letter.$queueName"; + } - public static function shortFormat(DomainEventSubscriber $subscriber): string - { - $subscriberCamelCaseName = (string) last(explode('\\', $subscriber::class)); + public static function shortFormat(DomainEventSubscriber $subscriber): string + { + $subscriberCamelCaseName = (string) last(explode('\\', $subscriber::class)); - return Utils::toSnakeCase($subscriberCamelCaseName); - } + return Utils::toSnakeCase($subscriberCamelCaseName); + } - private static function toSnakeCase(): callable - { - return static fn (string $text) => Utils::toSnakeCase($text); - } + private static function toSnakeCase(): callable + { + return static fn (string $text): string => Utils::toSnakeCase($text); + } } diff --git a/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php b/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php index b9e0bb1ab..c94edfdcc 100644 --- a/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php +++ b/src/Shared/Infrastructure/Bus/Event/WithMonitoring/WithPrometheusMonitoringEventBus.php @@ -7,28 +7,28 @@ use CodelyTv\Shared\Domain\Bus\Event\DomainEvent; use CodelyTv\Shared\Domain\Bus\Event\EventBus; use CodelyTv\Shared\Infrastructure\Monitoring\PrometheusMonitor; + use function Lambdish\Phunctional\each; -final class WithPrometheusMonitoringEventBus implements EventBus +final readonly class WithPrometheusMonitoringEventBus implements EventBus { - public function __construct( - private readonly PrometheusMonitor $monitor, - private readonly string $appName, - private readonly EventBus $bus - ) { - } - - public function publish(DomainEvent ...$events): void - { - $counter = $this->monitor->registry()->getOrRegisterCounter( - $this->appName, - 'domain_event', - 'Domain Events', - ['name'] - ); - - each(fn (DomainEvent $event) => $counter->inc(['name' => $event::eventName()]), $events); - - $this->bus->publish(...$events); - } + public function __construct( + private PrometheusMonitor $monitor, + private string $appName, + private EventBus $bus + ) {} + + public function publish(DomainEvent ...$events): void + { + $counter = $this->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/Query/InMemorySymfonyQueryBus.php b/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php index d01b9e105..4812214d0 100644 --- a/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php +++ b/src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php @@ -14,30 +14,28 @@ use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Stamp\HandledStamp; -final class InMemorySymfonyQueryBus implements QueryBus +final readonly class InMemorySymfonyQueryBus implements QueryBus { - private readonly MessageBus $bus; + private MessageBus $bus; - public function __construct(iterable $queryHandlers) - { - $this->bus = new MessageBus( - [ - new HandleMessageMiddleware( - new HandlersLocator(CallableFirstParameterExtractor::forCallables($queryHandlers)) - ), - ] - ); - } + public function __construct(iterable $queryHandlers) + { + $this->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); + 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); - } - } + 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 9f0d64588..c79d4859c 100644 --- a/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php +++ b/src/Shared/Infrastructure/Bus/Query/QueryNotRegisteredError.php @@ -9,10 +9,10 @@ final class QueryNotRegisteredError extends RuntimeException { - public function __construct(Query $query) - { - $queryClass = $query::class; + public function __construct(Query $query) + { + $queryClass = $query::class; - parent::__construct("The query <$queryClass> hasn't a query handler associated"); - } + parent::__construct("The query <$queryClass> has no associated query handler"); + } } 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 @@ +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)); - } + 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 index b671836cb..e76a46c3b 100644 --- a/src/Shared/Infrastructure/Doctrine/Dbal/DbalCustomTypesRegistrar.php +++ b/src/Shared/Infrastructure/Doctrine/Dbal/DbalCustomTypesRegistrar.php @@ -5,25 +5,28 @@ namespace CodelyTv\Shared\Infrastructure\Doctrine\Dbal; use Doctrine\DBAL\Types\Type; + use function Lambdish\Phunctional\each; final class DbalCustomTypesRegistrar { - private static bool $initialized = false; - - public static function register(array $customTypeClassNames): void - { - if (!self::$initialized) { - each(self::registerType(), $customTypeClassNames); - - self::$initialized = true; - } - } - - private static function registerType(): callable - { - return static function (string $customTypeClassName): void { - Type::addType($customTypeClassName::customTypeName(), $customTypeClassName); - }; - } + private static bool $initialized = false; + + public static function register(array $customTypeClassNames): void + { + if (!self::$initialized) { + each(self::registerType(), $customTypeClassNames); + + self::$initialized = true; + } + } + + private static function registerType(): callable + { + return static function (mixed $customTypeClassName): void { + $name = $customTypeClassName::customTypeName(); + + Type::addType($name, $customTypeClassName); + }; + } } diff --git a/src/Shared/Infrastructure/Doctrine/Dbal/DoctrineCustomType.php b/src/Shared/Infrastructure/Doctrine/Dbal/DoctrineCustomType.php index 38e761666..9a6cde085 100644 --- a/src/Shared/Infrastructure/Doctrine/Dbal/DoctrineCustomType.php +++ b/src/Shared/Infrastructure/Doctrine/Dbal/DoctrineCustomType.php @@ -6,5 +6,5 @@ interface DoctrineCustomType { - public static function customTypeName(): string; + public static function customTypeName(): string; } diff --git a/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php b/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php index f5a0cfcde..a19a5609e 100644 --- a/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php +++ b/src/Shared/Infrastructure/Doctrine/DoctrineEntityManagerFactory.php @@ -5,76 +5,87 @@ namespace CodelyTv\Shared\Infrastructure\Doctrine; use CodelyTv\Shared\Infrastructure\Doctrine\Dbal\DbalCustomTypesRegistrar; +use Doctrine\Common\EventManager; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Platforms\MariaDBPlatform; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\MySQLSchemaManager; use Doctrine\ORM\Configuration; -use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; use Doctrine\ORM\ORMSetup; use RuntimeException; + use function Lambdish\Phunctional\dissoc; final class DoctrineEntityManagerFactory { - 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) { - static::generateDatabaseIfNotExists($parameters, $schemaFile); - } - - DbalCustomTypesRegistrar::register($dbalCustomTypesClasses); - - return EntityManager::create($parameters, self::createConfiguration($contextPrefixes, $isDevMode)); - } - - 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->exec(sprintf('USE %s', $databaseName)); - $connection->exec(file_get_contents(realpath($schemaFile))); - } - - $connection->close(); - } - - 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 function createConfiguration(array $contextPrefixes, bool $isDevMode): Configuration - { - $config = ORMSetup::createConfiguration($isDevMode); - - $config->setMetadataDriverImpl(new SimplifiedXmlDriver(array_merge(self::$sharedPrefixes, $contextPrefixes))); - - return $config; - } + 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/Elasticsearch/ElasticsearchClient.php b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php index 1ead42d3e..41480e009 100644 --- a/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php +++ b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClient.php @@ -6,30 +6,28 @@ use Elasticsearch\Client; -final class ElasticsearchClient +final readonly class ElasticsearchClient { - public function __construct(private readonly Client $client, private readonly string $indexPrefix) - { - } + public function __construct(private Client $client, private string $indexPrefix) {} - public function persist(string $aggregateName, string $identifier, array $plainBody): void - { - $this->client->index( - [ - 'index' => sprintf('%s_%s', $this->indexPrefix, $aggregateName), - 'id' => $identifier, - 'body' => $plainBody, - ] - ); - } + public function persist(string $aggregateName, string $identifier, array $plainBody): void + { + $this->client->index( + [ + 'index' => sprintf('%s_%s', $this->indexPrefix, $aggregateName), + 'id' => $identifier, + 'body' => $plainBody, + ] + ); + } - public function client(): Client - { - return $this->client; - } + public function client(): Client + { + return $this->client; + } - public function indexPrefix(): string - { - return $this->indexPrefix; - } + public function indexPrefix(): string + { + return $this->indexPrefix; + } } diff --git a/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php index d18c90694..a5472afb9 100644 --- a/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php +++ b/src/Shared/Infrastructure/Elasticsearch/ElasticsearchClientFactory.php @@ -11,50 +11,50 @@ final class ElasticsearchClientFactory { - public function __invoke( - string $host, - string $indexPrefix, - string $schemasFolder, - string $environment - ): ElasticsearchClient { - $client = ClientBuilder::create()->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 ('prod' !== $environment) { - 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; - } - } + public function __invoke( + string $host, + string $indexPrefix, + string $schemasFolder, + string $environment + ): ElasticsearchClient { + $client = ClientBuilder::create()->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/Logger/MonologLogger.php b/src/Shared/Infrastructure/Logger/MonologLogger.php index 9dc205e6f..1e0928028 100644 --- a/src/Shared/Infrastructure/Logger/MonologLogger.php +++ b/src/Shared/Infrastructure/Logger/MonologLogger.php @@ -6,24 +6,22 @@ use CodelyTv\Shared\Domain\Logger; -final class MonologLogger implements Logger +final readonly class MonologLogger implements Logger { - public function __construct(private readonly \Monolog\Logger $logger) - { - } + public function __construct(private \Monolog\Logger $logger) {} - public function info(string $message, array $context = []): void - { - $this->logger->info($message, $context); - } + public function info(string $message, array $context = []): void + { + $this->logger->info($message, $context); + } - public function warning(string $message, array $context = []): void - { - $this->logger->warning($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); - } + 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 index 31e2a6adb..162c0482e 100644 --- a/src/Shared/Infrastructure/Monitoring/PrometheusMonitor.php +++ b/src/Shared/Infrastructure/Monitoring/PrometheusMonitor.php @@ -7,17 +7,17 @@ use Prometheus\CollectorRegistry; use Prometheus\Storage\APC; -final class PrometheusMonitor +final readonly class PrometheusMonitor { - private readonly CollectorRegistry $registry; + private CollectorRegistry $registry; - public function __construct() - { - $this->registry = new CollectorRegistry(new APC()); - } + public function __construct() + { + $this->registry = new CollectorRegistry(new APC()); + } - public function registry(): CollectorRegistry - { - return $this->registry; - } + public function registry(): CollectorRegistry + { + return $this->registry; + } } diff --git a/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php index 40767ac0c..88bd57083 100644 --- a/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php +++ b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineCriteriaConverter.php @@ -12,89 +12,88 @@ use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\Common\Collections\Expr\CompositeExpression; -final class DoctrineCriteriaConverter +final readonly class DoctrineCriteriaConverter { - public function __construct( - private readonly Criteria $criteria, - private readonly array $criteriaToDoctrineFields = [], - private readonly array $hydrators = [] - ) { - } - - public static function convert( - Criteria $criteria, - array $criteriaToDoctrineFields = [], - array $hydrators = [] - ): DoctrineCriteria { - $converter = new self($criteria, $criteriaToDoctrineFields, $hydrators); - - return $converter->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) - { - 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, string $value) - { - return $this->hydrators[$field]($value); - } + public function __construct( + private Criteria $criteria, + private array $criteriaToDoctrineFields = [], + private array $hydrators = [] + ) {} + + public static function convert( + Criteria $criteria, + array $criteriaToDoctrineFields = [], + array $hydrators = [] + ): DoctrineCriteria { + $converter = new self($criteria, $criteriaToDoctrineFields, $hydrators); + + return $converter->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 index 7ac1ea13f..dc6cdd9bb 100644 --- a/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineRepository.php +++ b/src/Shared/Infrastructure/Persistence/Doctrine/DoctrineRepository.php @@ -7,32 +7,40 @@ use CodelyTv\Shared\Domain\Aggregate\AggregateRoot; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Exception\NotSupported; abstract class DoctrineRepository { - public function __construct(private readonly 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 repository(string $entityClass): EntityRepository - { - return $this->entityManager->getRepository($entityClass); - } + public function __construct(private readonly 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); + } + + /** + * @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 index d9fd8b3ab..1ef8213bc 100644 --- a/src/Shared/Infrastructure/Persistence/Doctrine/UuidType.php +++ b/src/Shared/Infrastructure/Persistence/Doctrine/UuidType.php @@ -9,32 +9,33 @@ use CodelyTv\Shared\Infrastructure\Doctrine\Dbal\DoctrineCustomType; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\StringType; + use function Lambdish\Phunctional\last; abstract class UuidType extends StringType implements DoctrineCustomType { - abstract protected function typeClassName(): string; - - public static function customTypeName(): string - { - return Utils::toSnakeCase(str_replace('Type', '', (string) last(explode('\\', static::class)))); - } - - public function getName(): string - { - return self::customTypeName(); - } - - public function convertToPHPValue($value, AbstractPlatform $platform) - { - $className = $this->typeClassName(); - - return new $className($value); - } - - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - /** @var Uuid $value */ - return $value->value(); - } + abstract protected function typeClassName(): string; + + final public static function customTypeName(): string + { + return Utils::toSnakeCase(str_replace('Type', '', (string) last(explode('\\', static::class)))); + } + + final public function getName(): string + { + return self::customTypeName(); + } + + final public function convertToPHPValue($value, AbstractPlatform $platform): mixed + { + $className = $this->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 index 35aea009e..885066f5e 100644 --- a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticQueryGenerator.php +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticQueryGenerator.php @@ -6,52 +6,47 @@ use CodelyTv\Shared\Domain\Criteria\Filter; use CodelyTv\Shared\Domain\Criteria\FilterOperator; -use Exception; final class ElasticQueryGenerator { - private const MUST_TYPE = 'must'; - private const MUST_NOT_TYPE = 'must_not'; - private const TERM_TERM = 'term'; - private const TERM_RANGE = 'range'; - private const TERM_WILDCARD = 'wildcard'; - - private static array $mustNotFields = [FilterOperator::NOT_EQUAL, FilterOperator::NOT_CONTAINS]; - - public function __invoke(array $query, Filter $filter): array - { - $type = $this->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->value()) { - FilterOperator::EQUAL => self::TERM_TERM, - FilterOperator::NOT_EQUAL => '!=', - FilterOperator::GT, FilterOperator::LT => self::TERM_RANGE, - FilterOperator::CONTAINS, FilterOperator::NOT_CONTAINS => self::TERM_WILDCARD, - default => throw new Exception("Unexpected match value {$operator->value()}"), - }; - } + private const MUST_TYPE = 'must'; + private const MUST_NOT_TYPE = 'must_not'; + private const TERM_TERM = 'term'; + private const TERM_RANGE = 'range'; + private const TERM_WILDCARD = 'wildcard'; + + private static array $mustNotFields = [FilterOperator::NOT_EQUAL, FilterOperator::NOT_CONTAINS]; + + public function __invoke(array $query, Filter $filter): array + { + $type = $this->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 index bd8a5373b..257e531b3 100644 --- a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchCriteriaConverter.php +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchCriteriaConverter.php @@ -5,42 +5,49 @@ namespace CodelyTv\Shared\Infrastructure\Persistence\Elasticsearch; use CodelyTv\Shared\Domain\Criteria\Criteria; + use function Lambdish\Phunctional\reduce; final class ElasticsearchCriteriaConverter { - public function convert(Criteria $criteria): array - { - return [ - 'body' => 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 []; - } + public function convert(Criteria $criteria): array + { + return [ + 'body' => 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 index 1949acdbe..3236dd248 100644 --- a/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchRepository.php +++ b/src/Shared/Infrastructure/Persistence/Elasticsearch/ElasticsearchRepository.php @@ -7,56 +7,55 @@ use CodelyTv\Shared\Domain\Criteria\Criteria; use CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClient; use Elasticsearch\Common\Exceptions\Missing404Exception; + use function Lambdish\Phunctional\get_in; use function Lambdish\Phunctional\map; abstract class ElasticsearchRepository { - public function __construct(private readonly ElasticsearchClient $client) - { - } + public function __construct(private readonly ElasticsearchClient $client) {} - abstract protected function aggregateName(): string; + abstract protected function aggregateName(): string; - public function searchByCriteria(Criteria $criteria): array - { - $converter = new ElasticsearchCriteriaConverter(); + final public function searchByCriteria(Criteria $criteria): array + { + $converter = new ElasticsearchCriteriaConverter(); - $query = $converter->convert($criteria); + $query = $converter->convert($criteria); - return $this->searchRawElasticsearchQuery($query); - } + return $this->searchRawElasticsearchQuery($query); + } - protected function persist(string $id, array $plainBody): void - { - $this->client->persist($this->aggregateName(), $id, $plainBody); - } + protected function persist(string $id, array $plainBody): void + { + $this->client->persist($this->aggregateName(), $id, $plainBody); + } - protected function searchAllInElastic(): array - { - return $this->searchRawElasticsearchQuery([]); - } + 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)); + protected function searchRawElasticsearchQuery(array $params): array + { + try { + $result = $this->client->client()->search(array_merge(['index' => $this->indexName()], $params)); - $hits = get_in(['hits', 'hits'], $result, []); + $hits = (array) get_in(['hits', 'hits'], $result, []); - return map($this->elasticValuesExtractor(), $hits); - } catch (Missing404Exception) { - return []; - } - } + return map($this->elasticValuesExtractor(), $hits); + } catch (Missing404Exception) { + return []; + } + } - protected function indexName(): string - { - return sprintf('%s_%s', $this->client->indexPrefix(), $this->aggregateName()); - } + 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']; - } + private function elasticValuesExtractor(): callable + { + return static fn (array $elasticValues): array => $elasticValues['_source']; + } } diff --git a/src/Shared/Infrastructure/PhpRandomNumberGenerator.php b/src/Shared/Infrastructure/PhpRandomNumberGenerator.php index b01b0463d..b32a8be34 100644 --- a/src/Shared/Infrastructure/PhpRandomNumberGenerator.php +++ b/src/Shared/Infrastructure/PhpRandomNumberGenerator.php @@ -8,8 +8,8 @@ final class PhpRandomNumberGenerator implements RandomNumberGenerator { - public function generate(): int - { - return random_int(1, 5); - } + public function generate(): int + { + return random_int(1, 5); + } } diff --git a/src/Shared/Infrastructure/RamseyUuidGenerator.php b/src/Shared/Infrastructure/RamseyUuidGenerator.php index 36e46a640..8a8b703de 100644 --- a/src/Shared/Infrastructure/RamseyUuidGenerator.php +++ b/src/Shared/Infrastructure/RamseyUuidGenerator.php @@ -9,8 +9,8 @@ final class RamseyUuidGenerator implements UuidGenerator { - public function generate(): string - { - return Uuid::uuid4()->toString(); - } + public function generate(): string + { + return Uuid::uuid4()->toString(); + } } diff --git a/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php b/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php index a365d8c35..0930af5ee 100644 --- a/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php +++ b/src/Shared/Infrastructure/Symfony/AddJsonBodyToRequestListener.php @@ -11,30 +11,30 @@ final class AddJsonBodyToRequestListener { - public function onKernelRequest(RequestEvent $event): void - { - $request = $event->getRequest(); - $requestContents = $request->getContent(); + public function onKernelRequest(RequestEvent $event): void + { + $request = $event->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) => strtoupper($matches[1]), - $key - )] = $value; - } - $request->request->replace($jsonDataLowerCase); - } - } + 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); - } + 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 index ce5a18c76..09dd9be51 100644 --- a/src/Shared/Infrastructure/Symfony/ApiController.php +++ b/src/Shared/Infrastructure/Symfony/ApiController.php @@ -9,30 +9,31 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; use CodelyTv\Shared\Domain\Bus\Query\QueryBus; use CodelyTv\Shared\Domain\Bus\Query\Response; + use function Lambdish\Phunctional\each; abstract class ApiController { - public function __construct( - private readonly QueryBus $queryBus, - private readonly CommandBus $commandBus, - ApiExceptionsHttpStatusCodeMapping $exceptionHandler - ) { - each( - fn (int $httpCode, string $exceptionClass) => $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); - } + public function __construct( + private readonly QueryBus $queryBus, + private readonly CommandBus $commandBus, + ApiExceptionsHttpStatusCodeMapping $exceptionHandler + ) { + each( + fn (int $httpCode, string $exceptionClass) => $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 index 256d1e71a..d913d8766 100644 --- a/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php +++ b/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php @@ -6,37 +6,43 @@ use CodelyTv\Shared\Domain\DomainError; use CodelyTv\Shared\Domain\Utils; +use ReflectionClass; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Throwable; -final class ApiExceptionListener +final readonly class ApiExceptionListener { - public function __construct(private readonly ApiExceptionsHttpStatusCodeMapping $exceptionHandler) - { - } - - public function onException(ExceptionEvent $event): void - { - $exception = $event->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(Utils::extractClassName($error)); - } + public function __construct(private ApiExceptionsHttpStatusCodeMapping $exceptionHandler) {} + + public function onException(ExceptionEvent $event): void + { + $exception = $event->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 index 31a8b0586..8b1ad8a11 100644 --- a/src/Shared/Infrastructure/Symfony/ApiExceptionsHttpStatusCodeMapping.php +++ b/src/Shared/Infrastructure/Symfony/ApiExceptionsHttpStatusCodeMapping.php @@ -7,29 +7,30 @@ use InvalidArgumentException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + use function Lambdish\Phunctional\get; final class ApiExceptionsHttpStatusCodeMapping { - private const DEFAULT_STATUS_CODE = Response::HTTP_INTERNAL_SERVER_ERROR; - private array $exceptions = [ - InvalidArgumentException::class => 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 (null === $statusCode) { - throw new InvalidArgumentException("There are no status code mapping for <$exceptionClass>"); - } - - return $statusCode; - } + private const DEFAULT_STATUS_CODE = Response::HTTP_INTERNAL_SERVER_ERROR; + private array $exceptions = [ + InvalidArgumentException::class => 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 index 6b4a74093..0f3ad3aa8 100644 --- a/src/Shared/Infrastructure/Symfony/BasicHttpAuthMiddleware.php +++ b/src/Shared/Infrastructure/Symfony/BasicHttpAuthMiddleware.php @@ -12,51 +12,51 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; -final class BasicHttpAuthMiddleware +final readonly class BasicHttpAuthMiddleware { - public function __construct(private readonly CommandBus $bus) - { - } - - public function onKernelRequest(RequestEvent $event): void - { - $shouldAuthenticate = $event->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 null !== $user; - } - - private function authenticate(string $user, string $pass, RequestEvent $event): void - { - try { - $this->bus->dispatch(new AuthenticateUserCommand($user, $pass)); - - $this->addUserDataToRequest($user, $event); - } catch (InvalidAuthUsername | InvalidAuthCredentials) { - $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"']) - ); - } + public function __construct(private CommandBus $bus) {} + + public function onKernelRequest(RequestEvent $event): void + { + $shouldAuthenticate = $event->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/FlashSession.php b/src/Shared/Infrastructure/Symfony/FlashSession.php index 812c2a8b0..61bc6a523 100644 --- a/src/Shared/Infrastructure/Symfony/FlashSession.php +++ b/src/Shared/Infrastructure/Symfony/FlashSession.php @@ -9,34 +9,34 @@ final class FlashSession { - private static array $flashes = []; - - public function __construct(RequestStack $requestStack) - { - self::$flashes = Utils::dot($requestStack->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); - } + private static array $flashes = []; + + public function __construct(RequestStack $requestStack) + { + self::$flashes = Utils::dot($requestStack->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/WebController.php b/src/Shared/Infrastructure/Symfony/WebController.php index 81efc342d..741d764f0 100644 --- a/src/Shared/Infrastructure/Symfony/WebController.php +++ b/src/Shared/Infrastructure/Symfony/WebController.php @@ -16,59 +16,59 @@ abstract class WebController extends ApiController { - public function __construct( - private readonly Environment $twig, - private readonly RouterInterface $router, - private readonly RequestStack $requestStack, - QueryBus $queryBus, - CommandBus $commandBus, - ApiExceptionsHttpStatusCodeMapping $exceptionHandler - ) { - parent::__construct($queryBus, $commandBus, $exceptionHandler); - } + public function __construct( + private readonly Environment $twig, + private readonly RouterInterface $router, + private readonly RequestStack $requestStack, + QueryBus $queryBus, + CommandBus $commandBus, + ApiExceptionsHttpStatusCodeMapping $exceptionHandler + ) { + parent::__construct($queryBus, $commandBus, $exceptionHandler); + } - public function render(string $templatePath, array $arguments = []): SymfonyResponse - { - return new SymfonyResponse($this->twig->render($templatePath, $arguments)); - } + final public function render(string $templatePath, array $arguments = []): SymfonyResponse + { + return new SymfonyResponse($this->twig->render($templatePath, $arguments)); + } - public function redirect(string $routeName): RedirectResponse - { - return new RedirectResponse($this->router->generate($routeName), 302); - } + final public function redirect(string $routeName): RedirectResponse + { + return new RedirectResponse($this->router->generate($routeName), 302); + } - public function redirectWithMessage(string $routeName, string $message): RedirectResponse - { - $this->addFlashFor('message', [$message]); + final public function redirectWithMessage(string $routeName, string $message): RedirectResponse + { + $this->addFlashFor('message', [$message]); - return $this->redirect($routeName); - } + return $this->redirect($routeName); + } - public function redirectWithErrors( - string $routeName, - ConstraintViolationListInterface $errors, - Request $request - ): RedirectResponse { - $this->addFlashFor('errors', $this->formatFlashErrors($errors)); - $this->addFlashFor('inputs', $request->request->all()); + 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); - } + 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(); - } + private function formatFlashErrors(ConstraintViolationListInterface $violations): array + { + $errors = []; + foreach ($violations as $violation) { + $errors[str_replace(['[', ']'], ['', ''], $violation->getPropertyPath())] = $violation->getMessage(); + } - return $errors; - } + return $errors; + } - private function addFlashFor(string $prefix, array $messages): void - { - foreach ($messages as $key => $message) { - $this->requestStack->getSession()->getFlashBag()->set($prefix . '.' . $key, $message); - } - } + private function addFlashFor(string $prefix, array $messages): void + { + foreach ($messages as $key => $message) { + $this->requestStack->getSession()->getFlashBag()->set($prefix . '.' . $key, $message); + } + } } diff --git a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php index 0d83c9376..25787a7db 100644 --- a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php +++ b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandHandlerTest.php @@ -14,49 +14,49 @@ final class AuthenticateUserCommandHandlerTest extends AuthModuleUnitTestCase { - private AuthenticateUserCommandHandler|null $handler; + private AuthenticateUserCommandHandler | null $handler; - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $this->handler = new AuthenticateUserCommandHandler(new UserAuthenticator($this->repository())); - } + $this->handler = new AuthenticateUserCommandHandler(new UserAuthenticator($this->repository())); + } - /** @test */ - public function it_should_authenticate_a_valid_user(): void - { - $command = AuthenticateUserCommandMother::create(); - $authUser = AuthUserMother::fromCommand($command); + /** @test */ + public function it_should_authenticate_a_valid_user(): void + { + $command = AuthenticateUserCommandMother::create(); + $authUser = AuthUserMother::fromCommand($command); - $this->shouldSearch($authUser->username(), $authUser); + $this->shouldSearch($authUser->username(), $authUser); - $this->dispatch($command, $this->handler); - } + $this->dispatch($command, $this->handler); + } - /** @test */ - public function it_should_throw_an_exception_when_the_user_does_not_exist(): void - { - $this->expectException(InvalidAuthUsername::class); + /** @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()); + $command = AuthenticateUserCommandMother::create(); + $username = AuthUsernameMother::create($command->username()); - $this->shouldSearch($username); + $this->shouldSearch($username); - $this->dispatch($command, $this->handler); - } + $this->dispatch($command, $this->handler); + } - /** @test */ - public function it_should_throw_an_exception_when_the_password_does_not_math(): void - { - $this->expectException(InvalidAuthCredentials::class); + /** @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())); + $command = AuthenticateUserCommandMother::create(); + $authUser = AuthUserMother::create(username: AuthUsernameMother::create($command->username())); - $this->shouldSearch($authUser->username(), $authUser); + $this->shouldSearch($authUser->username(), $authUser); - $this->dispatch($command, $this->handler); - } + $this->dispatch($command, $this->handler); + } } diff --git a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php index d42c37d95..4418e2b9f 100644 --- a/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php +++ b/tests/Backoffice/Auth/Application/Authenticate/AuthenticateUserCommandMother.php @@ -12,13 +12,13 @@ final class AuthenticateUserCommandMother { - public static function create( - ?AuthUsername $username = null, - ?AuthPassword $password = null - ): AuthenticateUserCommand { - return new AuthenticateUserCommand( - $username?->value() ?? AuthUsernameMother::create()->value(), - $password?->value() ?? AuthPasswordMother::create()->value() - ); - } + public static function create( + ?AuthUsername $username = null, + ?AuthPassword $password = null + ): AuthenticateUserCommand { + return new AuthenticateUserCommand( + $username?->value() ?? AuthUsernameMother::create()->value(), + $password?->value() ?? AuthPasswordMother::create()->value() + ); + } } diff --git a/tests/Backoffice/Auth/AuthModuleUnitTestCase.php b/tests/Backoffice/Auth/AuthModuleUnitTestCase.php index 547d8e93c..4a7ad4a7c 100644 --- a/tests/Backoffice/Auth/AuthModuleUnitTestCase.php +++ b/tests/Backoffice/Auth/AuthModuleUnitTestCase.php @@ -12,19 +12,19 @@ abstract class AuthModuleUnitTestCase extends UnitTestCase { - private AuthRepository|MockInterface|null $repository; + private AuthRepository | MockInterface | null $repository = null; - protected function shouldSearch(AuthUsername $username, AuthUser $authUser = null): void - { - $this->repository() - ->shouldReceive('search') - ->with($this->similarTo($username)) - ->once() - ->andReturn($authUser); - } + protected function shouldSearch(AuthUsername $username, AuthUser $authUser = null): void + { + $this->repository() + ->shouldReceive('search') + ->with($this->similarTo($username)) + ->once() + ->andReturn($authUser); + } - protected function repository(): AuthRepository|MockInterface - { - return $this->repository = $this->repository ?? $this->mock(AuthRepository::class); - } + 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 index 6b344f818..9426c01c6 100644 --- a/tests/Backoffice/Auth/Domain/AuthPasswordMother.php +++ b/tests/Backoffice/Auth/Domain/AuthPasswordMother.php @@ -9,8 +9,8 @@ final class AuthPasswordMother { - public static function create(?string $value = null): AuthPassword - { - return new AuthPassword($value ?? UuidMother::create()); - } + public static function create(?string $value = null): AuthPassword + { + return new AuthPassword($value ?? UuidMother::create()); + } } diff --git a/tests/Backoffice/Auth/Domain/AuthUserMother.php b/tests/Backoffice/Auth/Domain/AuthUserMother.php index 0776c4838..76e3d34dc 100644 --- a/tests/Backoffice/Auth/Domain/AuthUserMother.php +++ b/tests/Backoffice/Auth/Domain/AuthUserMother.php @@ -11,16 +11,16 @@ final class AuthUserMother { - public static function create(?AuthUsername $username = null, ?AuthPassword $password = null): AuthUser - { - return new AuthUser($username ?? AuthUsernameMother::create(), $password ?? AuthPasswordMother::create()); - } + public static function create(?AuthUsername $username = null, ?AuthPassword $password = null): AuthUser + { + return new AuthUser($username ?? AuthUsernameMother::create(), $password ?? AuthPasswordMother::create()); + } - public static function fromCommand(AuthenticateUserCommand $command): AuthUser - { - return self::create( - AuthUsernameMother::create($command->username()), - AuthPasswordMother::create($command->password()) - ); - } + public static function fromCommand(AuthenticateUserCommand $command): AuthUser + { + return self::create( + AuthUsernameMother::create($command->username()), + AuthPasswordMother::create($command->password()) + ); + } } diff --git a/tests/Backoffice/Auth/Domain/AuthUsernameMother.php b/tests/Backoffice/Auth/Domain/AuthUsernameMother.php index 128f15382..988cedff5 100644 --- a/tests/Backoffice/Auth/Domain/AuthUsernameMother.php +++ b/tests/Backoffice/Auth/Domain/AuthUsernameMother.php @@ -9,8 +9,8 @@ final class AuthUsernameMother { - public static function create(?string $value = null): AuthUsername - { - return new AuthUsername($value ?? WordMother::create()); - } + public static function create(?string $value = null): AuthUsername + { + return new AuthUsername($value ?? WordMother::create()); + } } diff --git a/tests/Backoffice/Courses/BackofficeCoursesModuleInfrastructureTestCase.php b/tests/Backoffice/Courses/BackofficeCoursesModuleInfrastructureTestCase.php index b39b1b249..fe07bd4ae 100644 --- a/tests/Backoffice/Courses/BackofficeCoursesModuleInfrastructureTestCase.php +++ b/tests/Backoffice/Courses/BackofficeCoursesModuleInfrastructureTestCase.php @@ -11,13 +11,13 @@ abstract class BackofficeCoursesModuleInfrastructureTestCase extends BackofficeContextInfrastructureTestCase { - protected function mySqlRepository(): MySqlBackofficeCourseRepository - { - return new MySqlBackofficeCourseRepository($this->service(EntityManager::class)); - } + protected function mySqlRepository(): MySqlBackofficeCourseRepository + { + return new MySqlBackofficeCourseRepository($this->service(EntityManager::class)); + } - protected function elasticRepository(): ElasticsearchBackofficeCourseRepository - { - return $this->service(ElasticsearchBackofficeCourseRepository::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 index 85168553d..58e38d029 100644 --- a/tests/Backoffice/Courses/Domain/BackofficeCourseCriteriaMother.php +++ b/tests/Backoffice/Courses/Domain/BackofficeCourseCriteriaMother.php @@ -11,18 +11,16 @@ final class BackofficeCourseCriteriaMother { - public static function nameContains(string $text): Criteria - { - return CriteriaMother::create( - FiltersMother::createOne( - FilterMother::fromValues( - [ - 'field' => 'name', - 'operator' => 'CONTAINS', - 'value' => $text, - ] - ) - ) - ); - } + public static function nameContains(string $text): Criteria + { + return CriteriaMother::create( + FiltersMother::createOne( + FilterMother::fromValues([ + 'field' => 'name', + 'operator' => 'CONTAINS', + 'value' => $text, + ]) + ) + ); + } } diff --git a/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php b/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php index a28a35032..9d2ffbe35 100644 --- a/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php +++ b/tests/Backoffice/Courses/Domain/BackofficeCourseMother.php @@ -11,12 +11,12 @@ final class BackofficeCourseMother { - public static function create(?string $id = null, ?string $name = null, ?string $duration = null): BackofficeCourse - { - return new BackofficeCourse( - $id ?? CourseIdMother::create()->value(), - $name ?? CourseNameMother::create()->value(), - $duration ?? CourseDurationMother::create()->value() - ); - } + public static function create(?string $id = null, ?string $name = null, ?string $duration = null): BackofficeCourse + { + return new BackofficeCourse( + $id ?? CourseIdMother::create()->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 index 490e9d922..a45718666 100644 --- a/tests/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepositoryTest.php +++ b/tests/Backoffice/Courses/Infrastructure/Persistence/ElasticsearchBackofficeCourseRepositoryTest.php @@ -11,59 +11,56 @@ final class ElasticsearchBackofficeCourseRepositoryTest extends BackofficeCoursesModuleInfrastructureTestCase { - /** @test */ - public function it_should_save_a_valid_course(): void - { - $this->elasticRepository()->save(BackofficeCourseMother::create()); - } + /** @test */ + public function it_should_save_a_valid_course(): void + { + $this->elasticRepository()->save(BackofficeCourseMother::create()); + } - /** @test */ - public function it_should_search_all_existing_courses(): void - { - $existingCourse = BackofficeCourseMother::create(); - $anotherExistingCourse = BackofficeCourseMother::create(); - $existingCourses = [$existingCourse, $anotherExistingCourse]; + /** @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->elasticRepository()->save($existingCourse); + $this->elasticRepository()->save($anotherExistingCourse); - $this->eventually(fn () => $this->assertSimilar($existingCourses, $this->elasticRepository()->searchAll())); - } + $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]; + /** @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->elasticRepository()->save($existingCourse); + $this->elasticRepository()->save($anotherExistingCourse); - $this->eventually( - fn () => $this->assertSimilar( - $existingCourses, - $this->elasticRepository()->matching(CriteriaMother::empty()) - ) - ); - } + $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]; + /** @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'); + $nameContainsDddCriteria = BackofficeCourseCriteriaMother::nameContains('DDD'); - $this->elasticRepository()->save($dddInJavaCourse); - $this->elasticRepository()->save($dddInPhpCourse); - $this->elasticRepository()->save($intellijCourse); + $this->elasticRepository()->save($dddInJavaCourse); + $this->elasticRepository()->save($dddInPhpCourse); + $this->elasticRepository()->save($intellijCourse); - $this->eventually( - fn () => $this->assertSimilar($dddCourses, $this->elasticRepository()->matching($nameContainsDddCriteria)) - ); - } + $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 index bb35da789..300d01d99 100644 --- a/tests/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepositoryTest.php +++ b/tests/Backoffice/Courses/Infrastructure/Persistence/MySqlBackofficeCourseRepositoryTest.php @@ -11,54 +11,54 @@ final class MySqlBackofficeCourseRepositoryTest extends BackofficeCoursesModuleInfrastructureTestCase { - /** @test */ - public function it_should_save_a_valid_course(): void - { - $this->mySqlRepository()->save(BackofficeCourseMother::create()); - } + /** @test */ + public function it_should_save_a_valid_course(): void + { + $this->mySqlRepository()->save(BackofficeCourseMother::create()); + } - /** @test */ - public function it_should_search_all_existing_courses(): void - { - $existingCourse = BackofficeCourseMother::create(); - $anotherExistingCourse = BackofficeCourseMother::create(); - $existingCourses = [$existingCourse, $anotherExistingCourse]; + /** @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->mySqlRepository()->save($existingCourse); + $this->mySqlRepository()->save($anotherExistingCourse); - $this->assertSimilar($existingCourses, $this->mySqlRepository()->searchAll()); - } + $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]; + /** @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->mySqlRepository()->save($existingCourse); + $this->mySqlRepository()->save($anotherExistingCourse); + $this->clearUnitOfWork(); - $this->assertSimilar($existingCourses, $this->mySqlRepository()->matching(CriteriaMother::empty())); - } + $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]; + /** @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'); + $nameContainsDddCriteria = BackofficeCourseCriteriaMother::nameContains('DDD'); - $this->mySqlRepository()->save($dddInJavaCourse); - $this->mySqlRepository()->save($dddInPhpCourse); - $this->mySqlRepository()->save($intellijCourse); - $this->clearUnitOfWork(); + $this->mySqlRepository()->save($dddInJavaCourse); + $this->mySqlRepository()->save($dddInPhpCourse); + $this->mySqlRepository()->save($intellijCourse); + $this->clearUnitOfWork(); - $this->assertSimilar($dddCourses, $this->mySqlRepository()->matching($nameContainsDddCriteria)); - } + $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 index a64387798..0292a6623 100644 --- a/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeContextInfrastructureTestCase.php +++ b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeContextInfrastructureTestCase.php @@ -11,20 +11,20 @@ abstract class BackofficeContextInfrastructureTestCase extends InfrastructureTestCase { - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $arranger = new BackofficeEnvironmentArranger( - $this->service(ElasticsearchClient::class), - $this->service(EntityManager::class) - ); + $arranger = new BackofficeEnvironmentArranger( + $this->service(ElasticsearchClient::class), + $this->service(EntityManager::class) + ); - $arranger->arrange(); - } + $arranger->arrange(); + } - protected function kernelClass(): string - { - return BackofficeBackendKernel::class; - } + 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 index c042ffc2e..33a33ea05 100644 --- a/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeEnvironmentArranger.php +++ b/tests/Backoffice/Shared/Infraestructure/PhpUnit/BackofficeEnvironmentArranger.php @@ -9,21 +9,18 @@ use CodelyTv\Tests\Shared\Infrastructure\Doctrine\MySqlDatabaseCleaner; use CodelyTv\Tests\Shared\Infrastructure\Elastic\ElasticDatabaseCleaner; use Doctrine\ORM\EntityManager; + use function Lambdish\Phunctional\apply; -final class BackofficeEnvironmentArranger implements EnvironmentArranger +final readonly class BackofficeEnvironmentArranger implements EnvironmentArranger { - public function __construct(private ElasticsearchClient $elasticsearchClient, private EntityManager $entityManager) - { - } + public function __construct(private ElasticsearchClient $elasticsearchClient, private EntityManager $entityManager) {} - public function arrange(): void - { - apply(new ElasticDatabaseCleaner(), [$this->elasticsearchClient]); - apply(new MySqlDatabaseCleaner(), [$this->entityManager]); - } + public function arrange(): void + { + apply(new ElasticDatabaseCleaner(), [$this->elasticsearchClient]); + apply(new MySqlDatabaseCleaner(), [$this->entityManager]); + } - public function close(): void - { - } + public function close(): void {} } diff --git a/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php b/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php index 6a85c1f1a..4fe82e139 100644 --- a/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php +++ b/tests/Mooc/Courses/Application/Create/CreateCourseCommandHandlerTest.php @@ -12,26 +12,26 @@ final class CreateCourseCommandHandlerTest extends CoursesModuleUnitTestCase { - private CreateCourseCommandHandler|null $handler; + private CreateCourseCommandHandler | null $handler; - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $this->handler = new CreateCourseCommandHandler(new CourseCreator($this->repository(), $this->eventBus())); - } + $this->handler = new CreateCourseCommandHandler(new CourseCreator($this->repository(), $this->eventBus())); + } - /** @test */ - public function it_should_create_a_valid_course(): void - { - $command = CreateCourseCommandMother::create(); + /** @test */ + public function it_should_create_a_valid_course(): void + { + $command = CreateCourseCommandMother::create(); - $course = CourseMother::fromRequest($command); - $domainEvent = CourseCreatedDomainEventMother::fromCourse($course); + $course = CourseMother::fromRequest($command); + $domainEvent = CourseCreatedDomainEventMother::fromCourse($course); - $this->shouldSave($course); - $this->shouldPublishDomainEvent($domainEvent); + $this->shouldSave($course); + $this->shouldPublishDomainEvent($domainEvent); - $this->dispatch($command, $this->handler); - } + $this->dispatch($command, $this->handler); + } } diff --git a/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php b/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php index a1ba53fe2..9d4b31fea 100644 --- a/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php +++ b/tests/Mooc/Courses/Application/Create/CreateCourseCommandMother.php @@ -14,15 +14,15 @@ final class CreateCourseCommandMother { - public static function create( - ?CourseId $id = null, - ?CourseName $name = null, - ?CourseDuration $duration = null - ): CreateCourseCommand { - return new CreateCourseCommand( - $id?->value() ?? CourseIdMother::create()->value(), - $name?->value() ?? CourseNameMother::create()->value(), - $duration?->value() ?? CourseDurationMother::create()->value() - ); - } + public static function create( + ?CourseId $id = null, + ?CourseName $name = null, + ?CourseDuration $duration = null + ): CreateCourseCommand { + return new CreateCourseCommand( + $id?->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 index 2815aa7fe..7890b36f7 100644 --- a/tests/Mooc/Courses/Application/Update/CourseRenamerTest.php +++ b/tests/Mooc/Courses/Application/Update/CourseRenamerTest.php @@ -14,38 +14,38 @@ final class CourseRenamerTest extends CoursesModuleUnitTestCase { - private CourseRenamer|null $renamer; + private CourseRenamer | null $renamer; - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $this->renamer = new CourseRenamer($this->repository(), $this->eventBus()); - } + $this->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]); + /** @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->shouldSearch($course->id(), $course); + $this->shouldSave($renamedCourse); + $this->shouldNotPublishDomainEvent(); - $this->renamer->__invoke($course->id(), $newName); - } + $this->renamer->__invoke($course->id(), $newName); + } - /** @test */ - public function it_should_throw_an_exception_when_the_course_not_exist(): void - { - $this->expectException(CourseNotExist::class); + /** @test */ + public function it_should_throw_an_exception_when_the_course_not_exist(): void + { + $this->expectException(CourseNotExist::class); - $id = CourseIdMother::create(); + $id = CourseIdMother::create(); - $this->shouldSearch($id, null); + $this->shouldSearch($id, null); - $this->renamer->__invoke($id, CourseNameMother::create()); - } + $this->renamer->__invoke($id, CourseNameMother::create()); + } } diff --git a/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php b/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php index b81e7d9e8..67ad24223 100644 --- a/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php +++ b/tests/Mooc/Courses/CoursesModuleInfrastructureTestCase.php @@ -9,8 +9,8 @@ abstract class CoursesModuleInfrastructureTestCase extends MoocContextInfrastructureTestCase { - protected function repository(): CourseRepository - { - return $this->service(CourseRepository::class); - } + protected function repository(): CourseRepository + { + return $this->service(CourseRepository::class); + } } diff --git a/tests/Mooc/Courses/CoursesModuleUnitTestCase.php b/tests/Mooc/Courses/CoursesModuleUnitTestCase.php index 99aec4245..b4d269546 100644 --- a/tests/Mooc/Courses/CoursesModuleUnitTestCase.php +++ b/tests/Mooc/Courses/CoursesModuleUnitTestCase.php @@ -12,28 +12,28 @@ abstract class CoursesModuleUnitTestCase extends UnitTestCase { - private CourseRepository|MockInterface|null $repository; + private CourseRepository | MockInterface | null $repository = null; - protected function shouldSave(Course $course): void - { - $this->repository() - ->shouldReceive('save') - ->with($this->similarTo($course)) - ->once() - ->andReturnNull(); - } + protected function shouldSave(Course $course): void + { + $this->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 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->repository ?? $this->mock(CourseRepository::class); - } + 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 index 00044f5fe..b04f7109d 100644 --- a/tests/Mooc/Courses/Domain/CourseCreatedDomainEventMother.php +++ b/tests/Mooc/Courses/Domain/CourseCreatedDomainEventMother.php @@ -12,20 +12,20 @@ final class CourseCreatedDomainEventMother { - public static function create( - ?CourseId $id = null, - ?CourseName $name = null, - ?CourseDuration $duration = null - ): CourseCreatedDomainEvent { - return new CourseCreatedDomainEvent( - $id?->value() ?? CourseIdMother::create()->value(), - $name?->value() ?? CourseNameMother::create()->value(), - $duration?->value() ?? CourseDurationMother::create()->value() - ); - } + public static function create( + ?CourseId $id = null, + ?CourseName $name = null, + ?CourseDuration $duration = null + ): CourseCreatedDomainEvent { + return new CourseCreatedDomainEvent( + $id?->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()); - } + 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 index a3b78ae84..2cfc68c06 100644 --- a/tests/Mooc/Courses/Domain/CourseDurationMother.php +++ b/tests/Mooc/Courses/Domain/CourseDurationMother.php @@ -10,17 +10,17 @@ final class CourseDurationMother { - public static function create(?string $value = null): CourseDuration - { - return new CourseDuration($value ?? self::random()); - } + public static function create(?string $value = null): CourseDuration + { + return new CourseDuration($value ?? self::random()); + } - private static function random(): string - { - return sprintf( - '%s %s', - IntegerMother::lessThan(100), - RandomElementPicker::from('months', 'years', 'days', 'hours', 'minutes', 'seconds') - ); - } + private static function random(): string + { + return sprintf( + '%s %s', + IntegerMother::lessThan(100), + RandomElementPicker::from('months', 'years', 'days', 'hours', 'minutes', 'seconds') + ); + } } diff --git a/tests/Mooc/Courses/Domain/CourseIdMother.php b/tests/Mooc/Courses/Domain/CourseIdMother.php index f80119695..2b180910f 100644 --- a/tests/Mooc/Courses/Domain/CourseIdMother.php +++ b/tests/Mooc/Courses/Domain/CourseIdMother.php @@ -9,8 +9,8 @@ final class CourseIdMother { - public static function create(?string $value = null): CourseId - { - return new CourseId($value ?? UuidMother::create()); - } + public static function create(?string $value = null): CourseId + { + return new CourseId($value ?? UuidMother::create()); + } } diff --git a/tests/Mooc/Courses/Domain/CourseMother.php b/tests/Mooc/Courses/Domain/CourseMother.php index 31a622989..019770869 100644 --- a/tests/Mooc/Courses/Domain/CourseMother.php +++ b/tests/Mooc/Courses/Domain/CourseMother.php @@ -12,24 +12,24 @@ final class CourseMother { - public static function create( - ?CourseId $id = null, - ?CourseName $name = null, - ?CourseDuration $duration = null - ): Course { - return new Course( - $id ?? CourseIdMother::create(), - $name ?? CourseNameMother::create(), - $duration ?? CourseDurationMother::create() - ); - } + public static function create( + ?CourseId $id = null, + ?CourseName $name = null, + ?CourseDuration $duration = null + ): Course { + return new Course( + $id ?? CourseIdMother::create(), + $name ?? CourseNameMother::create(), + $duration ?? CourseDurationMother::create() + ); + } - public static function fromRequest(CreateCourseCommand $request): Course - { - return self::create( - CourseIdMother::create($request->id()), - CourseNameMother::create($request->name()), - CourseDurationMother::create($request->duration()) - ); - } + public static function fromRequest(CreateCourseCommand $request): Course + { + return self::create( + CourseIdMother::create($request->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 index 210496b49..72c4e7e02 100644 --- a/tests/Mooc/Courses/Domain/CourseNameMother.php +++ b/tests/Mooc/Courses/Domain/CourseNameMother.php @@ -9,8 +9,8 @@ final class CourseNameMother { - public static function create(?string $value = null): CourseName - { - return new CourseName($value ?? WordMother::create()); - } + public static function create(?string $value = null): CourseName + { + return new CourseName($value ?? WordMother::create()); + } } diff --git a/tests/Mooc/Courses/Infrastructure/Persistence/CourseRepositoryTest.php b/tests/Mooc/Courses/Infrastructure/Persistence/CourseRepositoryTest.php index 25b23b6b9..56f84522c 100644 --- a/tests/Mooc/Courses/Infrastructure/Persistence/CourseRepositoryTest.php +++ b/tests/Mooc/Courses/Infrastructure/Persistence/CourseRepositoryTest.php @@ -10,27 +10,27 @@ final class CourseRepositoryTest extends CoursesModuleInfrastructureTestCase { - /** @test */ - public function it_should_save_a_course(): void - { - $course = CourseMother::create(); + /** @test */ + public function it_should_save_a_course(): void + { + $course = CourseMother::create(); - $this->repository()->save($course); - } + $this->repository()->save($course); + } - /** @test */ - public function it_should_return_an_existing_course(): void - { - $course = CourseMother::create(); + /** @test */ + public function it_should_return_an_existing_course(): void + { + $course = CourseMother::create(); - $this->repository()->save($course); + $this->repository()->save($course); - $this->assertEquals($course, $this->repository()->search($course->id())); - } + $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())); - } + /** @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 index 7abaed048..07a588148 100644 --- a/tests/Mooc/CoursesCounter/Application/Find/CoursesCounterResponseMother.php +++ b/tests/Mooc/CoursesCounter/Application/Find/CoursesCounterResponseMother.php @@ -10,8 +10,8 @@ final class CoursesCounterResponseMother { - public static function create(?CoursesCounterTotal $total = null): CoursesCounterResponse - { - return new CoursesCounterResponse($total?->value() ?? CoursesCounterTotalMother::create()->value()); - } + public static function create(?CoursesCounterTotal $total = null): CoursesCounterResponse + { + return new CoursesCounterResponse($total?->value() ?? CoursesCounterTotalMother::create()->value()); + } } diff --git a/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php b/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php index 630137874..2c8bc2ef5 100644 --- a/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php +++ b/tests/Mooc/CoursesCounter/Application/Find/FindCoursesCounterQueryHandlerTest.php @@ -13,34 +13,34 @@ final class FindCoursesCounterQueryHandlerTest extends CoursesCounterModuleUnitTestCase { - private FindCoursesCounterQueryHandler|null $handler; + private FindCoursesCounterQueryHandler | null $handler; - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $this->handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder($this->repository())); - } + $this->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()); + /** @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->shouldSearch($counter); - $this->assertAskResponse($response, $query, $this->handler); - } + $this->assertAskResponse($response, $query, $this->handler); + } - /** @test */ - public function it_should_throw_an_exception_when_courses_counter_does_not_exists(): void - { - $query = new FindCoursesCounterQuery(); + /** @test */ + public function it_should_throw_an_exception_when_courses_counter_does_not_exists(): void + { + $query = new FindCoursesCounterQuery(); - $this->shouldSearch(null); + $this->shouldSearch(null); - $this->assertAskThrowsException(CoursesCounterNotExist::class, $query, $this->handler); - } + $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 index 9074bb70d..b81642fc2 100644 --- a/tests/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreatedTest.php +++ b/tests/Mooc/CoursesCounter/Application/Increment/IncrementCoursesCounterOnCourseCreatedTest.php @@ -14,65 +14,61 @@ final class IncrementCoursesCounterOnCourseCreatedTest extends CoursesCounterModuleUnitTestCase { - private IncrementCoursesCounterOnCourseCreated|null $subscriber; - - protected function setUp(): void - { - parent::setUp(); - - $this->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); - } + private IncrementCoursesCounterOnCourseCreated | null $subscriber; + + protected function setUp(): void + { + parent::setUp(); + + $this->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 index d990439dd..6937e3902 100644 --- a/tests/Mooc/CoursesCounter/CoursesCounterModuleUnitTestCase.php +++ b/tests/Mooc/CoursesCounter/CoursesCounterModuleUnitTestCase.php @@ -11,27 +11,27 @@ abstract class CoursesCounterModuleUnitTestCase extends UnitTestCase { - private CoursesCounterRepository|MockInterface|null $repository; + private CoursesCounterRepository | MockInterface | null $repository = null; - protected function shouldSave(CoursesCounter $course): void - { - $this->repository() - ->shouldReceive('save') - ->once() - ->with($this->similarTo($course)) - ->andReturnNull(); - } + protected function shouldSave(CoursesCounter $course): void + { + $this->repository() + ->shouldReceive('save') + ->once() + ->with($this->similarTo($course)) + ->andReturnNull(); + } - protected function shouldSearch(?CoursesCounter $counter): void - { - $this->repository() - ->shouldReceive('search') - ->once() - ->andReturn($counter); - } + protected function shouldSearch(?CoursesCounter $counter): void + { + $this->repository() + ->shouldReceive('search') + ->once() + ->andReturn($counter); + } - protected function repository(): CoursesCounterRepository|MockInterface - { - return $this->repository = $this->repository ?? $this->mock(CoursesCounterRepository::class); - } + 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 index e0dfc9c57..98bf659a1 100644 --- a/tests/Mooc/CoursesCounter/Domain/CoursesCounterIdMother.php +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterIdMother.php @@ -9,8 +9,8 @@ final class CoursesCounterIdMother { - public static function create(?string $value = null): CoursesCounterId - { - return new CoursesCounterId($value ?? UuidMother::create()); - } + public static function create(?string $value = null): CoursesCounterId + { + return new CoursesCounterId($value ?? UuidMother::create()); + } } diff --git a/tests/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEventMother.php b/tests/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEventMother.php index f3fc1e4d3..a3dc0b881 100644 --- a/tests/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEventMother.php +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterIncrementedDomainEventMother.php @@ -11,18 +11,18 @@ final class CoursesCounterIncrementedDomainEventMother { - public static function create( - ?CoursesCounterId $id = null, - ?CoursesCounterTotal $total = null - ): CoursesCounterIncrementedDomainEvent { - return new CoursesCounterIncrementedDomainEvent( - $id?->value() ?? CoursesCounterIdMother::create()->value(), - $total?->value() ?? CoursesCounterTotalMother::create()->value() - ); - } + public static function create( + ?CoursesCounterId $id = null, + ?CoursesCounterTotal $total = null + ): CoursesCounterIncrementedDomainEvent { + return new CoursesCounterIncrementedDomainEvent( + $id?->value() ?? CoursesCounterIdMother::create()->value(), + $total?->value() ?? CoursesCounterTotalMother::create()->value() + ); + } - public static function fromCounter(CoursesCounter $counter): CoursesCounterIncrementedDomainEvent - { - return self::create($counter->id(), $counter->total()); - } + 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 index ed8e836a4..d5200a5cd 100644 --- a/tests/Mooc/CoursesCounter/Domain/CoursesCounterMother.php +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterMother.php @@ -13,29 +13,29 @@ final class CoursesCounterMother { - public static function create( - ?CoursesCounterId $id = null, - ?CoursesCounterTotal $total = null, - CourseId ...$existingCourses - ): CoursesCounter { - return new CoursesCounter( - $id ?? CoursesCounterIdMother::create(), - $total ?? CoursesCounterTotalMother::create(), - ...count($existingCourses) ? $existingCourses : Repeater::random(fn () => CourseIdMother::create()) - ); - } + public static function create( + ?CoursesCounterId $id = null, + ?CoursesCounterTotal $total = null, + CourseId ...$existingCourses + ): CoursesCounter { + return new CoursesCounter( + $id ?? CoursesCounterIdMother::create(), + $total ?? CoursesCounterTotalMother::create(), + ...count($existingCourses) ? $existingCourses : Repeater::random(fn (): CourseId => CourseIdMother::create()) + ); + } - public static function withOne(CourseId $courseId): CoursesCounter - { - return self::create(CoursesCounterIdMother::create(), CoursesCounterTotalMother::one(), $courseId); - } + 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]) - ); - } + 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 index 49115ea90..0c29d090a 100644 --- a/tests/Mooc/CoursesCounter/Domain/CoursesCounterTotalMother.php +++ b/tests/Mooc/CoursesCounter/Domain/CoursesCounterTotalMother.php @@ -9,18 +9,18 @@ final class CoursesCounterTotalMother { - public static function create(?int $value = null): CoursesCounterTotal - { - return new CoursesCounterTotal($value ?? IntegerMother::create()); - } + public static function create(?int $value = null): CoursesCounterTotal + { + return new CoursesCounterTotal($value ?? IntegerMother::create()); + } - public static function one(): CoursesCounterTotal - { - return self::create(1); - } + public static function one(): CoursesCounterTotal + { + return self::create(1); + } - public static function random(): CoursesCounterTotal - { - return self::create(IntegerMother::create()); - } + public static function random(): CoursesCounterTotal + { + return self::create(IntegerMother::create()); + } } diff --git a/tests/Mooc/MoocArchitectureTest.php b/tests/Mooc/MoocArchitectureTest.php new file mode 100644 index 000000000..e1e57197f --- /dev/null +++ b/tests/Mooc/MoocArchitectureTest.php @@ -0,0 +1,56 @@ +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/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php index a793d0d7b..fb4bef7bf 100644 --- a/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php +++ b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocContextInfrastructureTestCase.php @@ -10,26 +10,26 @@ abstract class MoocContextInfrastructureTestCase extends InfrastructureTestCase { - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $arranger = new MoocEnvironmentArranger($this->service(EntityManager::class)); + $arranger = new MoocEnvironmentArranger($this->service(EntityManager::class)); - $arranger->arrange(); - } + $arranger->arrange(); + } - protected function tearDown(): void - { - $arranger = new MoocEnvironmentArranger($this->service(EntityManager::class)); + protected function tearDown(): void + { + $arranger = new MoocEnvironmentArranger($this->service(EntityManager::class)); - $arranger->close(); + $arranger->close(); - parent::tearDown(); - } + parent::tearDown(); + } - protected function kernelClass(): string - { - return MoocBackendKernel::class; - } + 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 index c6be3379d..39ddf5ef5 100644 --- a/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocEnvironmentArranger.php +++ b/tests/Mooc/Shared/Infrastructure/PhpUnit/MoocEnvironmentArranger.php @@ -7,20 +7,17 @@ use CodelyTv\Tests\Shared\Infrastructure\Arranger\EnvironmentArranger; use CodelyTv\Tests\Shared\Infrastructure\Doctrine\MySqlDatabaseCleaner; use Doctrine\ORM\EntityManager; + use function Lambdish\Phunctional\apply; -final class MoocEnvironmentArranger implements EnvironmentArranger +final readonly class MoocEnvironmentArranger implements EnvironmentArranger { - public function __construct(private EntityManager $entityManager) - { - } + public function __construct(private EntityManager $entityManager) {} - public function arrange(): void - { - apply(new MySqlDatabaseCleaner(), [$this->entityManager]); - } + public function arrange(): void + { + apply(new MySqlDatabaseCleaner(), [$this->entityManager]); + } - public function close(): void - { - } + 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/Shared/Domain/Criteria/CriteriaMother.php b/tests/Shared/Domain/Criteria/CriteriaMother.php index 4fe2bb6b6..b46d45089 100644 --- a/tests/Shared/Domain/Criteria/CriteriaMother.php +++ b/tests/Shared/Domain/Criteria/CriteriaMother.php @@ -10,17 +10,17 @@ final class CriteriaMother { - public static function create( - Filters $filters, - Order $order = null, - int $offset = null, - int $limit = null - ): Criteria { - return new Criteria($filters, $order ?: OrderMother::none(), $offset, $limit); - } + public static function create( + Filters $filters, + Order $order = null, + int $offset = null, + int $limit = null + ): Criteria { + return new Criteria($filters, $order ?: OrderMother::none(), $offset, $limit); + } - public static function empty(): Criteria - { - return self::create(FiltersMother::blank(), OrderMother::none()); - } + public static function empty(): Criteria + { + return self::create(FiltersMother::blank(), OrderMother::none()); + } } diff --git a/tests/Shared/Domain/Criteria/FilterFieldMother.php b/tests/Shared/Domain/Criteria/FilterFieldMother.php index 01f35645e..fdf89a4ae 100644 --- a/tests/Shared/Domain/Criteria/FilterFieldMother.php +++ b/tests/Shared/Domain/Criteria/FilterFieldMother.php @@ -9,8 +9,8 @@ final class FilterFieldMother { - public static function create(?string $fieldName = null): FilterField - { - return new FilterField($fieldName ?? WordMother::create()); - } + public static function create(?string $fieldName = null): FilterField + { + return new FilterField($fieldName ?? WordMother::create()); + } } diff --git a/tests/Shared/Domain/Criteria/FilterMother.php b/tests/Shared/Domain/Criteria/FilterMother.php index 9f3a6fcc5..ece793fd6 100644 --- a/tests/Shared/Domain/Criteria/FilterMother.php +++ b/tests/Shared/Domain/Criteria/FilterMother.php @@ -8,24 +8,38 @@ use CodelyTv\Shared\Domain\Criteria\FilterField; use CodelyTv\Shared\Domain\Criteria\FilterOperator; use CodelyTv\Shared\Domain\Criteria\FilterValue; +use CodelyTv\Tests\Shared\Domain\RandomElementPicker; final class FilterMother { - public static function create( - ?FilterField $field = null, - ?FilterOperator $operator = null, - ?FilterValue $value = null - ): Filter { - return new Filter( - $field ?? FilterFieldMother::create(), - $operator ?? FilterOperator::random(), - $value ?? FilterValueMother::create() - ); - } + public static function create( + ?FilterField $field = null, + ?FilterOperator $operator = null, + ?FilterValue $value = null + ): Filter { + return new Filter( + $field ?? FilterFieldMother::create(), + $operator ?? self::randomOperator(), + $value ?? FilterValueMother::create() + ); + } - /** @param string[] $values */ - public static function fromValues(array $values): Filter - { - return Filter::fromValues($values); - } + /** @param string[] $values */ + public static function fromValues(array $values): Filter + { + return Filter::fromValues($values); + } + + + private static function randomOperator(): FilterOperator + { + return RandomElementPicker::from( + FilterOperator::EQUAL, + FilterOperator::NOT_EQUAL, + FilterOperator::GT, + FilterOperator::LT, + FilterOperator::CONTAINS, + FilterOperator::NOT_CONTAINS + ); + } } diff --git a/tests/Shared/Domain/Criteria/FilterValueMother.php b/tests/Shared/Domain/Criteria/FilterValueMother.php index aa78e74a0..77c42c2e1 100644 --- a/tests/Shared/Domain/Criteria/FilterValueMother.php +++ b/tests/Shared/Domain/Criteria/FilterValueMother.php @@ -9,8 +9,8 @@ final class FilterValueMother { - public static function create(?string $value = null): FilterValue - { - return new FilterValue($value ?? WordMother::create()); - } + public static function create(?string $value = null): FilterValue + { + return new FilterValue($value ?? WordMother::create()); + } } diff --git a/tests/Shared/Domain/Criteria/FiltersMother.php b/tests/Shared/Domain/Criteria/FiltersMother.php index fe0009f5f..08678f850 100644 --- a/tests/Shared/Domain/Criteria/FiltersMother.php +++ b/tests/Shared/Domain/Criteria/FiltersMother.php @@ -9,19 +9,19 @@ final class FiltersMother { - /** @param Filter[] $filters */ - public static function create(array $filters): Filters - { - return new Filters($filters); - } + /** @param Filter[] $filters */ + public static function create(array $filters): Filters + { + return new Filters($filters); + } - public static function createOne(Filter $filter): Filters - { - return self::create([$filter]); - } + public static function createOne(Filter $filter): Filters + { + return self::create([$filter]); + } - public static function blank(): Filters - { - return self::create([]); - } + public static function blank(): Filters + { + return self::create([]); + } } diff --git a/tests/Shared/Domain/Criteria/OrderByMother.php b/tests/Shared/Domain/Criteria/OrderByMother.php index 457e35bf6..1a1cf2df3 100644 --- a/tests/Shared/Domain/Criteria/OrderByMother.php +++ b/tests/Shared/Domain/Criteria/OrderByMother.php @@ -9,8 +9,8 @@ final class OrderByMother { - public static function create(?string $fieldName = null): OrderBy - { - return new OrderBy($fieldName ?? WordMother::create()); - } + public static function create(?string $fieldName = null): OrderBy + { + return new OrderBy($fieldName ?? WordMother::create()); + } } diff --git a/tests/Shared/Domain/Criteria/OrderMother.php b/tests/Shared/Domain/Criteria/OrderMother.php index 10e791440..e574061ea 100644 --- a/tests/Shared/Domain/Criteria/OrderMother.php +++ b/tests/Shared/Domain/Criteria/OrderMother.php @@ -7,16 +7,22 @@ use CodelyTv\Shared\Domain\Criteria\Order; use CodelyTv\Shared\Domain\Criteria\OrderBy; use CodelyTv\Shared\Domain\Criteria\OrderType; +use CodelyTv\Tests\Shared\Domain\RandomElementPicker; final class OrderMother { - public static function create(?OrderBy $orderBy = null, ?OrderType $orderType = null): Order - { - return new Order($orderBy ?? OrderByMother::create(), $orderType ?? OrderType::random()); - } + public static function create(?OrderBy $orderBy = null, ?OrderType $orderType = null): Order + { + return new Order($orderBy ?? OrderByMother::create(), $orderType ?? self::randomOrderType()); + } - public static function none(): Order - { - return Order::none(); - } + public static function none(): Order + { + return Order::none(); + } + + private static function randomOrderType(): Order + { + return RandomElementPicker::from(OrderType::ASC, OrderType::DESC, OrderType::NONE); + } } diff --git a/tests/Shared/Domain/DuplicatorMother.php b/tests/Shared/Domain/DuplicatorMother.php index 2f23565ab..b22f40efa 100644 --- a/tests/Shared/Domain/DuplicatorMother.php +++ b/tests/Shared/Domain/DuplicatorMother.php @@ -6,25 +6,26 @@ use ReflectionObject; use ReflectionProperty; + use function Lambdish\Phunctional\each; final class DuplicatorMother { - public static function with($object, array $newParams): mixed - { - $duplicated = clone $object; - $reflection = new ReflectionObject($duplicated); + public static function with(mixed $object, array $newParams): mixed + { + $duplicated = clone $object; + $reflection = new ReflectionObject($duplicated); - each( - static function (ReflectionProperty $property) use ($duplicated, $newParams): void { - if (isset($newParams[$property->getName()])) { - $property->setAccessible(true); - $property->setValue($duplicated, $newParams[$property->getName()]); - } - }, - $reflection->getProperties() - ); + each( + static function (ReflectionProperty $property) use ($duplicated, $newParams): void { + if (isset($newParams[$property->getName()])) { + $property->setAccessible(true); + $property->setValue($duplicated, $newParams[$property->getName()]); + } + }, + $reflection->getProperties() + ); - return $duplicated; - } + return $duplicated; + } } diff --git a/tests/Shared/Domain/IntegerMother.php b/tests/Shared/Domain/IntegerMother.php index ac6259f3b..910a08cc8 100644 --- a/tests/Shared/Domain/IntegerMother.php +++ b/tests/Shared/Domain/IntegerMother.php @@ -6,18 +6,18 @@ final class IntegerMother { - public static function create(): int - { - return self::between(1); - } + public static function create(): int + { + return self::between(1); + } - public static function between(int $min, $max = PHP_INT_MAX): int - { - return MotherCreator::random()->numberBetween($min, $max); - } + public static function between(int $min, int $max = PHP_INT_MAX): int + { + return MotherCreator::random()->numberBetween($min, $max); + } - public static function lessThan(int $max): int - { - return self::between(1, $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 index 41eab9050..f2da13923 100644 --- a/tests/Shared/Domain/MotherCreator.php +++ b/tests/Shared/Domain/MotherCreator.php @@ -9,10 +9,10 @@ final class MotherCreator { - private static ?Generator $faker; + private static ?Generator $faker = null; - public static function random(): Generator - { - return self::$faker = self::$faker ?? Factory::create(); - } + public static function random(): Generator + { + return self::$faker ??= Factory::create(); + } } diff --git a/tests/Shared/Domain/RandomElementPicker.php b/tests/Shared/Domain/RandomElementPicker.php index baf41ecc8..76e8b1eda 100644 --- a/tests/Shared/Domain/RandomElementPicker.php +++ b/tests/Shared/Domain/RandomElementPicker.php @@ -6,8 +6,8 @@ final class RandomElementPicker { - public static function from(...$elements): mixed - { - return MotherCreator::random()->randomElement($elements); - } + public static function from(mixed ...$elements): mixed + { + return MotherCreator::random()->randomElement($elements); + } } diff --git a/tests/Shared/Domain/Repeater.php b/tests/Shared/Domain/Repeater.php index eef4fdbc2..19781e14f 100644 --- a/tests/Shared/Domain/Repeater.php +++ b/tests/Shared/Domain/Repeater.php @@ -8,13 +8,13 @@ final class Repeater { - public static function repeat(callable $function, int $quantity): array - { - return repeat($function, $quantity); - } + public static function repeat(callable $function, int $quantity): array + { + return repeat($function, $quantity); + } - public static function random(callable $function): array - { - return self::repeat($function, IntegerMother::lessThan(5)); - } + public static function random(callable $function): array + { + return self::repeat($function, IntegerMother::lessThan(5)); + } } diff --git a/tests/Shared/Domain/TestUtils.php b/tests/Shared/Domain/TestUtils.php index cfb347deb..141bfea5e 100644 --- a/tests/Shared/Domain/TestUtils.php +++ b/tests/Shared/Domain/TestUtils.php @@ -9,22 +9,22 @@ final class TestUtils { - public static function isSimilar($expected, $actual): bool - { - $constraint = new CodelyTvConstraintIsSimilar($expected); + public static function isSimilar(mixed $expected, mixed $actual): bool + { + $constraint = new CodelyTvConstraintIsSimilar($expected); - return $constraint->evaluate($actual, '', true); - } + return $constraint->evaluate($actual, '', true); + } - public static function assertSimilar($expected, $actual): void - { - $constraint = new CodelyTvConstraintIsSimilar($expected); + public static function assertSimilar(mixed $expected, mixed $actual): void + { + $constraint = new CodelyTvConstraintIsSimilar($expected); - $constraint->evaluate($actual); - } + $constraint->evaluate($actual); + } - public static function similarTo($value, $delta = 0.0): CodelyTvMatcherIsSimilar - { - return new CodelyTvMatcherIsSimilar($value, $delta); - } + 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 index 524e2f72e..20bd3516c 100644 --- a/tests/Shared/Domain/UuidMother.php +++ b/tests/Shared/Domain/UuidMother.php @@ -6,8 +6,8 @@ final class UuidMother { - public static function create(): string - { - return MotherCreator::random()->unique()->uuid; - } + public static function create(): string + { + return MotherCreator::random()->unique()->uuid; + } } diff --git a/tests/Shared/Domain/WordMother.php b/tests/Shared/Domain/WordMother.php index 424fe1774..4bf988e35 100644 --- a/tests/Shared/Domain/WordMother.php +++ b/tests/Shared/Domain/WordMother.php @@ -6,8 +6,8 @@ final class WordMother { - public static function create(): string - { - return MotherCreator::random()->word; - } + public static function create(): string + { + return MotherCreator::random()->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($method, $url): void - { - $this->request->sendRequest($method, $this->locatePath($url)); - } - - /** - * @Given I send a :method request to :url with body: - */ - public function iSendARequestToWithBody($method, $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 !== $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($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)); - } + private readonly MinkHelper $sessionHelper; + private readonly MinkSessionRequestHelper $request; + + public function __construct(private readonly Session $minkSession) + { + $this->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 index 2d70452cb..29d6788a8 100644 --- a/tests/Shared/Infrastructure/Behat/ApplicationFeatureContext.php +++ b/tests/Shared/Infrastructure/Behat/ApplicationFeatureContext.php @@ -10,29 +10,28 @@ use CodelyTv\Shared\Infrastructure\Bus\Event\InMemory\InMemorySymfonyEventBus; use CodelyTv\Shared\Infrastructure\Doctrine\DatabaseConnections; -final class ApplicationFeatureContext implements Context +final readonly class ApplicationFeatureContext implements Context { - public function __construct( - private DatabaseConnections $connections, - private InMemorySymfonyEventBus $bus, - private DomainEventJsonDeserializer $deserializer - ) { - } + public function __construct( + private DatabaseConnections $connections, + private InMemorySymfonyEventBus $bus, + private DomainEventJsonDeserializer $deserializer + ) {} - /** @BeforeScenario */ - public function cleanEnvironment(): void - { - $this->connections->clear(); - $this->connections->truncate(); - } + /** @BeforeScenario */ + public function cleanEnvironment(): void + { + $this->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()); + /** + * @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); - } + $this->bus->publish($domainEvent); + } } diff --git a/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php b/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php index bf13218ba..d009d2a79 100644 --- a/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php +++ b/tests/Shared/Infrastructure/Bus/Command/FakeCommand.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\Bus\Command\Command; -final class FakeCommand implements Command -{ -} +final class FakeCommand implements Command {} diff --git a/tests/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBusTest.php b/tests/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBusTest.php index 3586816a1..b8dbff2b2 100644 --- a/tests/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBusTest.php +++ b/tests/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBusTest.php @@ -13,43 +13,43 @@ final class InMemorySymfonyCommandBusTest extends UnitTestCase { - private InMemorySymfonyCommandBus|null $commandBus; - - protected function setUp(): void - { - parent::setUp(); - - $this->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): void - { - throw new RuntimeException('This works fine!'); - } - }; - } - - private function command(): Command|MockInterface - { - return $this->mock(Command::class); - } + private InMemorySymfonyCommandBus | null $commandBus; + + protected function setUp(): void + { + parent::setUp(); + + $this->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 index d2d4457e1..ca17d6ceb 100644 --- a/tests/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBusTest.php +++ b/tests/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBusTest.php @@ -16,36 +16,36 @@ final class MySqlDoctrineEventBusTest extends InfrastructureTestCase { - private MySqlDoctrineEventBus|null $bus; - private MySqlDoctrineDomainEventsConsumer|null $consumer; - - protected function setUp(): void - { - parent::setUp(); - - $this->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; - } + private MySqlDoctrineEventBus | null $bus; + private MySqlDoctrineDomainEventsConsumer | null $consumer; + + protected function setUp(): void + { + parent::setUp(); + + $this->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 index 0934f42bc..b3d5b09e8 100644 --- a/tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php +++ b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBusTest.php @@ -21,164 +21,161 @@ final class RabbitMqEventBusTest extends InfrastructureTestCase { - private $connection; - private $exchangeName; - private $configurer; - private $publisher; - private $consumer; - private $fakeSubscriber; - private $consumerHasBeenExecuted; + private mixed $connection; + private string $exchangeName; + private RabbitMqConfigurer $configurer; + private RabbitMqEventBus $publisher; + private RabbitMqDomainEventsConsumer $consumer; + private TestAllWorksOnRabbitMqEventsPublished $fakeSubscriber; + private bool $consumerHasBeenExecuted; - protected function setUp(): void - { - parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - $this->connection = $this->service(RabbitMqConnection::class); + $this->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->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); - } + $this->cleanEnvironment($this->connection); + } - /** @test */ - public function it_should_publish_and_consume_domain_events_from_rabbitmq(): void - { - $domainEvent = CourseCreatedDomainEventMother::create(); + /** @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->configurer->configure($this->exchangeName, $this->fakeSubscriber); - $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); - } + $this->publisher->publish($domainEvent); - 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->consumer->consume( + $this->assertConsumer($domainEvent), + RabbitMqQueueNameFormatter::format($this->fakeSubscriber) + ); - $this->consumerHasBeenExecuted = true; - }; - } + $this->assertTrue($this->consumerHasBeenExecuted); + } - private function failingConsumer(): callable - { - return static function (DomainEvent $domainEvent): void { - throw new RuntimeException('To test'); - }; - } + /** @test */ + public function it_should_throw_an_exception_consuming_non_existing_domain_events(): void + { + $this->expectException(RuntimeException::class); - private function simulateErrorConsuming(): void - { - try { - $this->consumer->consume( - $this->failingConsumer(), - RabbitMqQueueNameFormatter::format($this->fakeSubscriber) - ); - } catch (Throwable $error) { - $this->assertInstanceOf(RuntimeException::class, $error); - } - } + $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); - 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(); - } + $this->simulateErrorConsuming(); - private function assertDeadLetterContainsEvent(int $expectedNumberOfEvents): void - { - $totalEventsInDeadLetter = 0; + $this->assertDeadLetterContainsEvent(1); + } - while ($this->connection->queue(RabbitMqQueueNameFormatter::formatDeadLetter($this->fakeSubscriber))->get( - AMQP_AUTOACK - )) { - $totalEventsInDeadLetter++; - } + protected function kernelClass(): string + { + return MoocBackendKernel::class; + } - $this->assertSame($expectedNumberOfEvents, $totalEventsInDeadLetter); - } + 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 index 310d3ce58..937f28e08 100644 --- a/tests/Shared/Infrastructure/Bus/Event/RabbitMq/TestAllWorksOnRabbitMqEventsPublished.php +++ b/tests/Shared/Infrastructure/Bus/Event/RabbitMq/TestAllWorksOnRabbitMqEventsPublished.php @@ -10,15 +10,10 @@ final class TestAllWorksOnRabbitMqEventsPublished implements DomainEventSubscriber { - public static function subscribedTo(): array - { - return [ - CourseCreatedDomainEvent::class, - CoursesCounterIncrementedDomainEvent::class, - ]; - } + public static function subscribedTo(): array + { + return [CourseCreatedDomainEvent::class, CoursesCounterIncrementedDomainEvent::class, ]; + } - public function __invoke(CourseCreatedDomainEvent|CoursesCounterIncrementedDomainEvent $event): void - { - } + public function __invoke(CourseCreatedDomainEvent | CoursesCounterIncrementedDomainEvent $event): void {} } diff --git a/tests/Shared/Infrastructure/Bus/Query/FakeQuery.php b/tests/Shared/Infrastructure/Bus/Query/FakeQuery.php index 0afc89517..4f5f2ba62 100644 --- a/tests/Shared/Infrastructure/Bus/Query/FakeQuery.php +++ b/tests/Shared/Infrastructure/Bus/Query/FakeQuery.php @@ -6,6 +6,4 @@ use CodelyTv\Shared\Domain\Bus\Query\Query; -final class FakeQuery implements Query -{ -} +final class FakeQuery implements Query {} diff --git a/tests/Shared/Infrastructure/Bus/Query/FakeResponse.php b/tests/Shared/Infrastructure/Bus/Query/FakeResponse.php index 36b437b5e..41386dca5 100644 --- a/tests/Shared/Infrastructure/Bus/Query/FakeResponse.php +++ b/tests/Shared/Infrastructure/Bus/Query/FakeResponse.php @@ -6,14 +6,12 @@ use CodelyTv\Shared\Domain\Bus\Query\Response; -final class FakeResponse implements Response +final readonly class FakeResponse implements Response { - public function __construct(private int $number) - { - } + public function __construct(private int $number) {} - public function number(): int - { - return $this->number; - } + public function number(): int + { + return $this->number; + } } diff --git a/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php b/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php index 48fd55d26..dc3f2095b 100644 --- a/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php +++ b/tests/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBusTest.php @@ -13,43 +13,43 @@ final class InMemorySymfonyQueryBusTest extends UnitTestCase { - private InMemorySymfonyQueryBus|null $queryBus; - - protected function setUp(): void - { - parent::setUp(); - - $this->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): void - { - throw new RuntimeException('This works fine!'); - } - }; - } - - private function query(): Query|MockInterface - { - return $this->mock(Query::class); - } + private InMemorySymfonyQueryBus | null $queryBus; + + protected function setUp(): void + { + parent::setUp(); + + $this->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 index 2a5ef9eb7..810e216cc 100644 --- a/tests/Shared/Infrastructure/ConstantRandomNumberGenerator.php +++ b/tests/Shared/Infrastructure/ConstantRandomNumberGenerator.php @@ -8,8 +8,8 @@ final class ConstantRandomNumberGenerator implements RandomNumberGenerator { - public function generate(): int - { - return 1; - } + public function generate(): int + { + return 1; + } } diff --git a/tests/Shared/Infrastructure/Doctrine/MySqlDatabaseCleaner.php b/tests/Shared/Infrastructure/Doctrine/MySqlDatabaseCleaner.php index 19058ab66..3c7cf7afc 100644 --- a/tests/Shared/Infrastructure/Doctrine/MySqlDatabaseCleaner.php +++ b/tests/Shared/Infrastructure/Doctrine/MySqlDatabaseCleaner.php @@ -6,35 +6,36 @@ use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManagerInterface; + use function Lambdish\Phunctional\first; use function Lambdish\Phunctional\map; final class MySqlDatabaseCleaner { - public function __invoke(EntityManagerInterface $entityManager): void - { - $connection = $entityManager->getConnection(); + public function __invoke(EntityManagerInterface $entityManager): void + { + $connection = $entityManager->getConnection(); - $tables = $this->tables($connection); - $truncateTablesSql = $this->truncateDatabaseSql($tables); + $tables = $this->tables($connection); + $truncateTablesSql = $this->truncateDatabaseSql($tables); - $connection->exec($truncateTablesSql); - } + $connection->executeQuery($truncateTablesSql); + } - private function truncateDatabaseSql(array $tables): string - { - $truncateTables = map($this->truncateTableSql(), $tables); + 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)); - } + 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`;', first($table)); - } + private function truncateTableSql(): callable + { + return fn (array $table): string => sprintf('TRUNCATE TABLE `%s`;', (string) first($table)); + } - private function tables(Connection $connection): array - { - return $connection->query('SHOW TABLES')->fetchAll(); - } + 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 index 651ede7f6..293d5284a 100644 --- a/tests/Shared/Infrastructure/Elastic/ElasticDatabaseCleaner.php +++ b/tests/Shared/Infrastructure/Elastic/ElasticDatabaseCleaner.php @@ -5,22 +5,23 @@ namespace CodelyTv\Tests\Shared\Infrastructure\Elastic; use CodelyTv\Shared\Infrastructure\Elasticsearch\ElasticsearchClient; + use function Lambdish\Phunctional\each; final class ElasticDatabaseCleaner { - public function __invoke(ElasticsearchClient $client): void - { - $indices = $client->client()->cat()->indices(); + public function __invoke(ElasticsearchClient $client): void + { + $indices = $client->client()->cat()->indices(); - each( - static function (array $index) use ($client): void { - $indexName = $index['index']; + each( + static function (array $index) use ($client): void { + $indexName = $index['index']; - $client->client()->indices()->delete(['index' => $indexName]); - $client->client()->indices()->create(['index' => $indexName]); - }, - $indices - ); - } + $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 index 81d4407c5..ea443d6e1 100644 --- a/tests/Shared/Infrastructure/Mink/MinkHelper.php +++ b/tests/Shared/Infrastructure/Mink/MinkHelper.php @@ -9,84 +9,72 @@ use Symfony\Component\BrowserKit\AbstractBrowser; use Symfony\Component\DomCrawler\Crawler; -final class MinkHelper +final readonly class MinkHelper { - public function __construct(private Session $session) - { - } - - 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 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)); - } - - private function resetRequestStuff(): void - { - $this->getSession()->reset(); - $this->resetServerParameters(); - } + public function __construct(private Session $session) {} + + public function sendRequest(string $method, string $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'] + ); + + 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 index 9cc8525c6..df914dd98 100644 --- a/tests/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php +++ b/tests/Shared/Infrastructure/Mink/MinkSessionRequestHelper.php @@ -7,24 +7,22 @@ use Behat\Gherkin\Node\PyStringNode; use Symfony\Component\DomCrawler\Crawler; -final class MinkSessionRequestHelper +final readonly class MinkSessionRequestHelper { - public function __construct(private $sessionHelper) - { - } + public function __construct(private MinkHelper $sessionHelper) {} - public function sendRequest($method, $url, array $optionalParams = []): void - { - $this->request($method, $url, $optionalParams); - } + public function sendRequest($method, $url, array $optionalParams = []): void + { + $this->request($method, $url, $optionalParams); + } - public function sendRequestWithPyStringNode($method, $url, PyStringNode $body): void - { - $this->request($method, $url, ['content' => $body->getRaw()]); - } + public function sendRequestWithPyStringNode($method, $url, PyStringNode $body): void + { + $this->request($method, $url, ['content' => $body->getRaw()]); + } - public function request($method, $url, array $optionalParams = []): Crawler - { - return $this->sessionHelper->sendRequest($method, $url, $optionalParams); - } + 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 index 056fe1e26..1737b7adb 100644 --- a/tests/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php +++ b/tests/Shared/Infrastructure/Mockery/CodelyTvMatcherIsSimilar.php @@ -5,26 +5,25 @@ namespace CodelyTv\Tests\Shared\Infrastructure\Mockery; use CodelyTv\Tests\Shared\Infrastructure\PhpUnit\Constraint\CodelyTvConstraintIsSimilar; -use Mockery\Matcher\MatcherAbstract; +use Mockery\Matcher\MatcherInterface; +use Stringable; -final class CodelyTvMatcherIsSimilar extends MatcherAbstract +final readonly class CodelyTvMatcherIsSimilar implements Stringable, MatcherInterface { - private CodelyTvConstraintIsSimilar $constraint; - - public function __construct($value, $delta = 0.0) - { - parent::__construct($value); - - $this->constraint = new CodelyTvConstraintIsSimilar($value, $delta); - } - - public function match(&$actual): bool - { - return $this->constraint->evaluate($actual, '', true); - } - - public function __toString(): string - { - return 'Is similar'; - } + private CodelyTvConstraintIsSimilar $constraint; + + public function __construct(mixed $value, float $delta = 0.0) + { + $this->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 index afa3a6f18..22ee1fccf 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootArraySimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootArraySimilarComparator.php @@ -8,41 +8,42 @@ use CodelyTv\Tests\Shared\Domain\TestUtils; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Comparator\ComparisonFailure; + use function Lambdish\Phunctional\all; use function Lambdish\Phunctional\any; use function Lambdish\Phunctional\instance_of; final class AggregateRootArraySimilarComparator extends Comparator { - public function accepts($expected, $actual): bool - { - return is_array($expected) - && is_array($actual) - && (all(instance_of(AggregateRoot::class), $expected) - && all(instance_of(AggregateRoot::class), $actual)); - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void - { - if (!$this->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) => any( - fn (AggregateRoot $actual) => TestUtils::isSimilar($expected, $actual), - $actualArray - ); - - return all($exists, $expectedArray); - } + public function accepts($expected, $actual): bool + { + return is_array($expected) + && is_array($actual) + && (all(instance_of(AggregateRoot::class), $expected) + && all(instance_of(AggregateRoot::class), $actual)); + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + if (!$this->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 index 01317fa01..aefd86a36 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootSimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/AggregateRootSimilarComparator.php @@ -12,63 +12,63 @@ final class AggregateRootSimilarComparator extends Comparator { - public function accepts($expected, $actual): bool - { - $aggregateRootClass = AggregateRoot::class; - - return $expected instanceof $aggregateRootClass && $actual instanceof $aggregateRootClass; - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void - { - $actualEntity = clone $actual; - $actualEntity->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; - } + public function accepts($expected, $actual): bool + { + $aggregateRootClass = AggregateRoot::class; + + return $expected instanceof $aggregateRootClass && $actual instanceof $aggregateRootClass; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + $actualEntity = clone $actual; + $actualEntity->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 index a6ca2c917..67d3b5605 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeSimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeSimilarComparator.php @@ -12,39 +12,39 @@ final class DateTimeSimilarComparator extends ObjectComparator { - public function accepts($expected, $actual): bool - { - return $expected instanceof DateTimeInterface && $actual instanceof DateTimeInterface; - } - - public function assertEquals( - $expected, - $actual, - $delta = 0.0, - $canonicalize = false, - $ignoreCase = false, - array &$processed = [] - ): void { - $normalizedDelta = $delta === 0.0 ? 10 : $delta; - $intervalWithDelta = new DateInterval(sprintf('PT%sS', abs($normalizedDelta))); - - $expectedLower = clone $expected; - $expectedUpper = clone $expected; - - if ($actual < $expectedLower->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'; - } + public function accepts($expected, $actual): bool + { + return $expected instanceof DateTimeInterface && $actual instanceof DateTimeInterface; + } + + public function assertEquals( + $expected, + $actual, + $delta = 0.0, + $canonicalize = false, + $ignoreCase = false, + array &$processed = [] + ): void { + $normalizedDelta = $delta === 0.0 ? 10 : $delta; + $intervalWithDelta = new DateInterval(sprintf('PT%sS', abs($normalizedDelta))); + + $expectedLower = clone $expected; + $expectedUpper = clone $expected; + + if ($actual < $expectedLower->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 index 4e1ea83ff..546c75cb9 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeStringSimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DateTimeStringSimilarComparator.php @@ -14,59 +14,58 @@ final class DateTimeStringSimilarComparator extends ObjectComparator { - public function accepts($expected, $actual): bool - { - return (null !== $actual) - && is_string($expected) - && is_string($actual) - && $this->isValidDateTimeString($expected) - && $this->isValidDateTimeString($actual); - } + public function accepts($expected, $actual): bool + { + return is_string($expected) + && is_string($actual) + && $this->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); + 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))); + $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.' - ); - } - } + 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); + protected function dateTimeToString(DateTimeInterface $datetime): string + { + $string = $datetime->format(DateTime::ATOM); - return $string ?: 'Invalid DateTime object'; - } + return $string ?: 'Invalid DateTime object'; + } - private function isValidDateTimeString(string $expected): bool - { - $isValid = true; + private function isValidDateTimeString(string $expected): bool + { + $isValid = true; - try { - new DateTimeImmutable($expected); - } catch (Throwable) { - $isValid = false; - } + try { + new DateTimeImmutable($expected); + } catch (Throwable) { + $isValid = false; + } - return $isValid; - } + return $isValid; + } } diff --git a/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php index b7dd6773f..c191f7fb0 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventArraySimilarComparator.php @@ -8,41 +8,42 @@ use CodelyTv\Tests\Shared\Domain\TestUtils; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Comparator\ComparisonFailure; + use function Lambdish\Phunctional\all; use function Lambdish\Phunctional\any; use function Lambdish\Phunctional\instance_of; final class DomainEventArraySimilarComparator extends Comparator { - public function accepts($expected, $actual): bool - { - return is_array($expected) - && is_array($actual) - && (all(instance_of(DomainEvent::class), $expected) - && all(instance_of(DomainEvent::class), $actual)); - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void - { - if (!$this->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) => any( - static fn (DomainEvent $actual) => TestUtils::isSimilar($expected, $actual), - $actualArray - ); - - return all($exists, $expectedArray); - } + public function accepts($expected, $actual): bool + { + return is_array($expected) + && is_array($actual) + && (all(instance_of(DomainEvent::class), $expected) + && all(instance_of(DomainEvent::class), $actual)); + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + if (!$this->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 index 48a52274a..f14cb6a77 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventSimilarComparator.php +++ b/tests/Shared/Infrastructure/PhpUnit/Comparator/DomainEventSimilarComparator.php @@ -12,64 +12,64 @@ final class DomainEventSimilarComparator extends Comparator { - private static array $ignoredAttributes = ['eventId', 'occurredOn']; - - public function accepts($expected, $actual): bool - { - $domainEventRootClass = DomainEvent::class; - - return $expected instanceof $domainEventRootClass && $actual instanceof $domainEventRootClass; - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void - { - if (!$this->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; - } + private static array $ignoredAttributes = ['eventId', 'occurredOn']; + + public function accepts($expected, $actual): bool + { + $domainEventRootClass = DomainEvent::class; + + return $expected instanceof $domainEventRootClass && $actual instanceof $domainEventRootClass; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + if (!$this->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 index 7a19a089c..e5b4ca53a 100644 --- a/tests/Shared/Infrastructure/PhpUnit/Constraint/CodelyTvConstraintIsSimilar.php +++ b/tests/Shared/Infrastructure/PhpUnit/Constraint/CodelyTvConstraintIsSimilar.php @@ -14,76 +14,62 @@ use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Comparator\Factory; + use function is_string; use function sprintf; // Based on \PHPUnit\Framework\Constraint\IsEqual final class CodelyTvConstraintIsSimilar extends Constraint { - public function __construct(private $value, private float $delta = 0.0) - { - } - - public function evaluate($other, $description = '', $returnResult = false): bool - { - if ($this->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 - ); - } + public function __construct(private $value, private readonly float $delta = 0.0) {} + + public function evaluate($other, $description = '', $returnResult = false): bool + { + if ($this->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 index f16af1891..52438a8f0 100644 --- a/tests/Shared/Infrastructure/PhpUnit/InfrastructureTestCase.php +++ b/tests/Shared/Infrastructure/PhpUnit/InfrastructureTestCase.php @@ -11,49 +11,54 @@ abstract class InfrastructureTestCase extends KernelTestCase { - abstract protected function kernelClass(): string; - - protected function setUp(): void - { - $_SERVER['KERNEL_CLASS'] = $this->kernelClass(); - - self::bootKernel(['environment' => 'test']); - - parent::setUp(); - } - - protected function assertSimilar($expected, $actual): void - { - TestUtils::assertSimilar($expected, $actual); - } - - protected function service(string $id): mixed - { - return self::getContainer()->get($id); - } - - protected function parameter($parameter): mixed - { - return self::getContainer()->getParameter($parameter); - } - - protected function clearUnitOfWork(): void - { - $this->service(EntityManager::class)->clear(); - } - - protected function eventually(callable $fn, $totalRetries = 3, $timeToWaitOnErrorInSeconds = 1, $attempt = 0): void - { - try { - $fn(); - } catch (Throwable $error) { - if ($totalRetries === $attempt) { - throw $error; - } - - sleep($timeToWaitOnErrorInSeconds); - - $this->eventually($fn, $totalRetries, $timeToWaitOnErrorInSeconds, $attempt + 1); - } - } + abstract protected function kernelClass(): string; + + protected function setUp(): void + { + $_SERVER['KERNEL_CLASS'] = $this->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 index ce26b3a80..599f2005d 100644 --- a/tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php +++ b/tests/Shared/Infrastructure/PhpUnit/UnitTestCase.php @@ -11,92 +11,94 @@ use CodelyTv\Shared\Domain\Bus\Query\Response; use CodelyTv\Shared\Domain\UuidGenerator; use CodelyTv\Tests\Shared\Domain\TestUtils; +use CodelyTv\Tests\Shared\Infrastructure\Mockery\CodelyTvMatcherIsSimilar; use Mockery; use Mockery\Adapter\Phpunit\MockeryTestCase; -use Mockery\Matcher\MatcherAbstract; use Mockery\MockInterface; +use Throwable; abstract class UnitTestCase extends MockeryTestCase { - private EventBus|MockInterface|null $eventBus; - private UuidGenerator|MockInterface|null $uuidGenerator; - - protected function mock(string $className): MockInterface - { - return Mockery::mock($className); - } - - protected function shouldPublishDomainEvent(DomainEvent $domainEvent): void - { - $this->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->eventBus ?? $this->mock(EventBus::class); - } - - protected function shouldGenerateUuid(string $uuid): void - { - $this->uuidGenerator() - ->shouldReceive('generate') - ->once() - ->withNoArgs() - ->andReturn($uuid); - } - - protected function uuidGenerator(): UuidGenerator|MockInterface - { - return $this->uuidGenerator = $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); - } - - protected function assertAskThrowsException(string $expectedErrorClass, Query $query, callable $queryHandler): void - { - $this->expectException($expectedErrorClass); - - $queryHandler($query); - } - - protected function isSimilar($expected, $actual): bool - { - return TestUtils::isSimilar($expected, $actual); - } - - protected function assertSimilar($expected, $actual): void - { - TestUtils::assertSimilar($expected, $actual); - } - - protected function similarTo($value, $delta = 0.0): MatcherAbstract - { - return TestUtils::similarTo($value, $delta); - } + private EventBus | MockInterface | null $eventBus = null; + private MockInterface | UuidGenerator | null $uuidGenerator = null; + + protected function mock(string $className): MockInterface + { + return Mockery::mock($className); + } + + protected function shouldPublishDomainEvent(DomainEvent $domainEvent): void + { + $this->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(); + } +}