From 30227b112851629f9b564e2fc299459104c1e5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:29:41 +0100 Subject: [PATCH 01/32] ci: remove travis file --- .travis.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1f33380..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: php - -sudo: false - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - -before_script: - - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi - - composer install --dev From 33883c9f84f56324f7d94034b69f2897421a1dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:29:51 +0100 Subject: [PATCH 02/32] composer: remove branch alias --- composer.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composer.json b/composer.json index 7679a40..9b102cc 100644 --- a/composer.json +++ b/composer.json @@ -39,10 +39,5 @@ "Beberlei\\Metrics\\": "src/Beberlei/Metrics", "Beberlei\\Bundle\\MetricsBundle\\": "src/Beberlei/Bundle/MetricsBundle" } - }, - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } } } From 6940144b53a0f4738c5569d7432577146117518e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:39:04 +0100 Subject: [PATCH 03/32] composer: re-organise it a bit --- composer.json | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 9b102cc..c60f8b8 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "name": "beberlei/metrics", "description": "Simple library to talk to metrics collector services.", "keywords": ["metrics", "logging"], + "license": "MIT", "authors": [ { "name": "Benjamin Eberlei", @@ -14,13 +15,6 @@ "role": "Lead Developer" } ], - "license": "MIT", - "suggest": { - "corley/influxdb-sdk": "For InfluxDB integration", - "okitsu/zabbix-sender": "For zabbix integration", - "kriswallsmith/buzz": "For Librato integration", - "jimdo/prometheus_client_php": "For Prometheus integration" - }, "require": { "psr/log": "^1.0 || ^2.0 || ^3.0" }, @@ -39,5 +33,14 @@ "Beberlei\\Metrics\\": "src/Beberlei/Metrics", "Beberlei\\Bundle\\MetricsBundle\\": "src/Beberlei/Bundle/MetricsBundle" } + }, + "suggest": { + "corley/influxdb-sdk": "For InfluxDB integration", + "okitsu/zabbix-sender": "For zabbix integration", + "kriswallsmith/buzz": "For Librato integration", + "jimdo/prometheus_client_php": "For Prometheus integration" + }, + "config": { + "sort-packages": true } } From 7276758190863a37ffee07ba9cef5ae0fd556424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:40:53 +0100 Subject: [PATCH 04/32] composer: simplify autoloader --- composer.json | 4 ++-- src/{Beberlei => }/Metrics/Collector/Collector.php | 0 src/{Beberlei => }/Metrics/Collector/DoctrineDBAL.php | 0 src/{Beberlei => }/Metrics/Collector/DogStatsD.php | 0 src/{Beberlei => }/Metrics/Collector/GaugeableCollector.php | 0 src/{Beberlei => }/Metrics/Collector/Graphite.php | 0 src/{Beberlei => }/Metrics/Collector/InMemory.php | 0 src/{Beberlei => }/Metrics/Collector/InfluxDB.php | 0 .../Metrics/Collector/InlineTaggableGaugeableCollector.php | 0 .../Collector/InlineTaggableGaugeableNullCollector.php | 0 src/{Beberlei => }/Metrics/Collector/Librato.php | 0 src/{Beberlei => }/Metrics/Collector/Logger.php | 0 src/{Beberlei => }/Metrics/Collector/Null.php | 0 src/{Beberlei => }/Metrics/Collector/NullCollector.php | 0 src/{Beberlei => }/Metrics/Collector/Prometheus.php | 0 src/{Beberlei => }/Metrics/Collector/StatsD.php | 0 src/{Beberlei => }/Metrics/Collector/TaggableCollector.php | 0 src/{Beberlei => }/Metrics/Collector/Telegraf.php | 0 src/{Beberlei => }/Metrics/Collector/Zabbix.php | 0 src/{Beberlei => }/Metrics/Factory.php | 0 src/{Beberlei => }/Metrics/MetricsException.php | 0 src/{Beberlei => }/Metrics/Tests/Collector/InMemoryTest.php | 0 src/{Beberlei => }/Metrics/Tests/Collector/InfluxDBTest.php | 0 src/{Beberlei => }/Metrics/Tests/Collector/PrometheusTest.php | 0 src/{Beberlei => }/Metrics/Tests/FactoryTest.php | 0 .../Bundle => }/MetricsBundle/BeberleiMetricsBundle.php | 0 .../DependencyInjection/BeberleiMetricsExtension.php | 0 .../MetricsBundle/DependencyInjection/Configuration.php | 0 .../Bundle => }/MetricsBundle/Resources/config/metrics.xml | 0 .../DependencyInjection/BeberleiMetricsExtensionTest.php | 0 30 files changed, 2 insertions(+), 2 deletions(-) rename src/{Beberlei => }/Metrics/Collector/Collector.php (100%) rename src/{Beberlei => }/Metrics/Collector/DoctrineDBAL.php (100%) rename src/{Beberlei => }/Metrics/Collector/DogStatsD.php (100%) rename src/{Beberlei => }/Metrics/Collector/GaugeableCollector.php (100%) rename src/{Beberlei => }/Metrics/Collector/Graphite.php (100%) rename src/{Beberlei => }/Metrics/Collector/InMemory.php (100%) rename src/{Beberlei => }/Metrics/Collector/InfluxDB.php (100%) rename src/{Beberlei => }/Metrics/Collector/InlineTaggableGaugeableCollector.php (100%) rename src/{Beberlei => }/Metrics/Collector/InlineTaggableGaugeableNullCollector.php (100%) rename src/{Beberlei => }/Metrics/Collector/Librato.php (100%) rename src/{Beberlei => }/Metrics/Collector/Logger.php (100%) rename src/{Beberlei => }/Metrics/Collector/Null.php (100%) rename src/{Beberlei => }/Metrics/Collector/NullCollector.php (100%) rename src/{Beberlei => }/Metrics/Collector/Prometheus.php (100%) rename src/{Beberlei => }/Metrics/Collector/StatsD.php (100%) rename src/{Beberlei => }/Metrics/Collector/TaggableCollector.php (100%) rename src/{Beberlei => }/Metrics/Collector/Telegraf.php (100%) rename src/{Beberlei => }/Metrics/Collector/Zabbix.php (100%) rename src/{Beberlei => }/Metrics/Factory.php (100%) rename src/{Beberlei => }/Metrics/MetricsException.php (100%) rename src/{Beberlei => }/Metrics/Tests/Collector/InMemoryTest.php (100%) rename src/{Beberlei => }/Metrics/Tests/Collector/InfluxDBTest.php (100%) rename src/{Beberlei => }/Metrics/Tests/Collector/PrometheusTest.php (100%) rename src/{Beberlei => }/Metrics/Tests/FactoryTest.php (100%) rename src/{Beberlei/Bundle => }/MetricsBundle/BeberleiMetricsBundle.php (100%) rename src/{Beberlei/Bundle => }/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php (100%) rename src/{Beberlei/Bundle => }/MetricsBundle/DependencyInjection/Configuration.php (100%) rename src/{Beberlei/Bundle => }/MetricsBundle/Resources/config/metrics.xml (100%) rename src/{Beberlei/Bundle => }/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php (100%) diff --git a/composer.json b/composer.json index c60f8b8..04f2e12 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,8 @@ }, "autoload": { "psr-4": { - "Beberlei\\Metrics\\": "src/Beberlei/Metrics", - "Beberlei\\Bundle\\MetricsBundle\\": "src/Beberlei/Bundle/MetricsBundle" + "Beberlei\\Metrics\\": "src/Metrics", + "Beberlei\\Bundle\\MetricsBundle\\": "src/MetricsBundle" } }, "suggest": { diff --git a/src/Beberlei/Metrics/Collector/Collector.php b/src/Metrics/Collector/Collector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Collector.php rename to src/Metrics/Collector/Collector.php diff --git a/src/Beberlei/Metrics/Collector/DoctrineDBAL.php b/src/Metrics/Collector/DoctrineDBAL.php similarity index 100% rename from src/Beberlei/Metrics/Collector/DoctrineDBAL.php rename to src/Metrics/Collector/DoctrineDBAL.php diff --git a/src/Beberlei/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php similarity index 100% rename from src/Beberlei/Metrics/Collector/DogStatsD.php rename to src/Metrics/Collector/DogStatsD.php diff --git a/src/Beberlei/Metrics/Collector/GaugeableCollector.php b/src/Metrics/Collector/GaugeableCollector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/GaugeableCollector.php rename to src/Metrics/Collector/GaugeableCollector.php diff --git a/src/Beberlei/Metrics/Collector/Graphite.php b/src/Metrics/Collector/Graphite.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Graphite.php rename to src/Metrics/Collector/Graphite.php diff --git a/src/Beberlei/Metrics/Collector/InMemory.php b/src/Metrics/Collector/InMemory.php similarity index 100% rename from src/Beberlei/Metrics/Collector/InMemory.php rename to src/Metrics/Collector/InMemory.php diff --git a/src/Beberlei/Metrics/Collector/InfluxDB.php b/src/Metrics/Collector/InfluxDB.php similarity index 100% rename from src/Beberlei/Metrics/Collector/InfluxDB.php rename to src/Metrics/Collector/InfluxDB.php diff --git a/src/Beberlei/Metrics/Collector/InlineTaggableGaugeableCollector.php b/src/Metrics/Collector/InlineTaggableGaugeableCollector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/InlineTaggableGaugeableCollector.php rename to src/Metrics/Collector/InlineTaggableGaugeableCollector.php diff --git a/src/Beberlei/Metrics/Collector/InlineTaggableGaugeableNullCollector.php b/src/Metrics/Collector/InlineTaggableGaugeableNullCollector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/InlineTaggableGaugeableNullCollector.php rename to src/Metrics/Collector/InlineTaggableGaugeableNullCollector.php diff --git a/src/Beberlei/Metrics/Collector/Librato.php b/src/Metrics/Collector/Librato.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Librato.php rename to src/Metrics/Collector/Librato.php diff --git a/src/Beberlei/Metrics/Collector/Logger.php b/src/Metrics/Collector/Logger.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Logger.php rename to src/Metrics/Collector/Logger.php diff --git a/src/Beberlei/Metrics/Collector/Null.php b/src/Metrics/Collector/Null.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Null.php rename to src/Metrics/Collector/Null.php diff --git a/src/Beberlei/Metrics/Collector/NullCollector.php b/src/Metrics/Collector/NullCollector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/NullCollector.php rename to src/Metrics/Collector/NullCollector.php diff --git a/src/Beberlei/Metrics/Collector/Prometheus.php b/src/Metrics/Collector/Prometheus.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Prometheus.php rename to src/Metrics/Collector/Prometheus.php diff --git a/src/Beberlei/Metrics/Collector/StatsD.php b/src/Metrics/Collector/StatsD.php similarity index 100% rename from src/Beberlei/Metrics/Collector/StatsD.php rename to src/Metrics/Collector/StatsD.php diff --git a/src/Beberlei/Metrics/Collector/TaggableCollector.php b/src/Metrics/Collector/TaggableCollector.php similarity index 100% rename from src/Beberlei/Metrics/Collector/TaggableCollector.php rename to src/Metrics/Collector/TaggableCollector.php diff --git a/src/Beberlei/Metrics/Collector/Telegraf.php b/src/Metrics/Collector/Telegraf.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Telegraf.php rename to src/Metrics/Collector/Telegraf.php diff --git a/src/Beberlei/Metrics/Collector/Zabbix.php b/src/Metrics/Collector/Zabbix.php similarity index 100% rename from src/Beberlei/Metrics/Collector/Zabbix.php rename to src/Metrics/Collector/Zabbix.php diff --git a/src/Beberlei/Metrics/Factory.php b/src/Metrics/Factory.php similarity index 100% rename from src/Beberlei/Metrics/Factory.php rename to src/Metrics/Factory.php diff --git a/src/Beberlei/Metrics/MetricsException.php b/src/Metrics/MetricsException.php similarity index 100% rename from src/Beberlei/Metrics/MetricsException.php rename to src/Metrics/MetricsException.php diff --git a/src/Beberlei/Metrics/Tests/Collector/InMemoryTest.php b/src/Metrics/Tests/Collector/InMemoryTest.php similarity index 100% rename from src/Beberlei/Metrics/Tests/Collector/InMemoryTest.php rename to src/Metrics/Tests/Collector/InMemoryTest.php diff --git a/src/Beberlei/Metrics/Tests/Collector/InfluxDBTest.php b/src/Metrics/Tests/Collector/InfluxDBTest.php similarity index 100% rename from src/Beberlei/Metrics/Tests/Collector/InfluxDBTest.php rename to src/Metrics/Tests/Collector/InfluxDBTest.php diff --git a/src/Beberlei/Metrics/Tests/Collector/PrometheusTest.php b/src/Metrics/Tests/Collector/PrometheusTest.php similarity index 100% rename from src/Beberlei/Metrics/Tests/Collector/PrometheusTest.php rename to src/Metrics/Tests/Collector/PrometheusTest.php diff --git a/src/Beberlei/Metrics/Tests/FactoryTest.php b/src/Metrics/Tests/FactoryTest.php similarity index 100% rename from src/Beberlei/Metrics/Tests/FactoryTest.php rename to src/Metrics/Tests/FactoryTest.php diff --git a/src/Beberlei/Bundle/MetricsBundle/BeberleiMetricsBundle.php b/src/MetricsBundle/BeberleiMetricsBundle.php similarity index 100% rename from src/Beberlei/Bundle/MetricsBundle/BeberleiMetricsBundle.php rename to src/MetricsBundle/BeberleiMetricsBundle.php diff --git a/src/Beberlei/Bundle/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php similarity index 100% rename from src/Beberlei/Bundle/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php rename to src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php diff --git a/src/Beberlei/Bundle/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php similarity index 100% rename from src/Beberlei/Bundle/MetricsBundle/DependencyInjection/Configuration.php rename to src/MetricsBundle/DependencyInjection/Configuration.php diff --git a/src/Beberlei/Bundle/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml similarity index 100% rename from src/Beberlei/Bundle/MetricsBundle/Resources/config/metrics.xml rename to src/MetricsBundle/Resources/config/metrics.xml diff --git a/src/Beberlei/Bundle/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php b/src/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php similarity index 100% rename from src/Beberlei/Bundle/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php rename to src/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php From ba19a515245796c90bf067c840e6b70fda01832b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:42:45 +0100 Subject: [PATCH 05/32] composer: move tests to it's own folder --- composer.json | 5 +++++ .../Tests => tests/Metrics}/Collector/InMemoryTest.php | 0 .../Tests => tests/Metrics}/Collector/InfluxDBTest.php | 0 .../Tests => tests/Metrics}/Collector/PrometheusTest.php | 0 {src/Metrics/Tests => tests/Metrics}/FactoryTest.php | 0 .../DependencyInjection/BeberleiMetricsExtensionTest.php | 0 6 files changed, 5 insertions(+) rename {src/Metrics/Tests => tests/Metrics}/Collector/InMemoryTest.php (100%) rename {src/Metrics/Tests => tests/Metrics}/Collector/InfluxDBTest.php (100%) rename {src/Metrics/Tests => tests/Metrics}/Collector/PrometheusTest.php (100%) rename {src/Metrics/Tests => tests/Metrics}/FactoryTest.php (100%) rename {src/MetricsBundle/Tests => tests/MetricsBundle}/DependencyInjection/BeberleiMetricsExtensionTest.php (100%) diff --git a/composer.json b/composer.json index 04f2e12..95ceaea 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,11 @@ "Beberlei\\Bundle\\MetricsBundle\\": "src/MetricsBundle" } }, + "autoload-dev": { + "psr-4": { + "Beberlei\\Bundle\\MetricsBundle\\": "src/MetricsBundle" + } + }, "suggest": { "corley/influxdb-sdk": "For InfluxDB integration", "okitsu/zabbix-sender": "For zabbix integration", diff --git a/src/Metrics/Tests/Collector/InMemoryTest.php b/tests/Metrics/Collector/InMemoryTest.php similarity index 100% rename from src/Metrics/Tests/Collector/InMemoryTest.php rename to tests/Metrics/Collector/InMemoryTest.php diff --git a/src/Metrics/Tests/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php similarity index 100% rename from src/Metrics/Tests/Collector/InfluxDBTest.php rename to tests/Metrics/Collector/InfluxDBTest.php diff --git a/src/Metrics/Tests/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php similarity index 100% rename from src/Metrics/Tests/Collector/PrometheusTest.php rename to tests/Metrics/Collector/PrometheusTest.php diff --git a/src/Metrics/Tests/FactoryTest.php b/tests/Metrics/FactoryTest.php similarity index 100% rename from src/Metrics/Tests/FactoryTest.php rename to tests/Metrics/FactoryTest.php diff --git a/src/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php similarity index 100% rename from src/MetricsBundle/Tests/DependencyInjection/BeberleiMetricsExtensionTest.php rename to tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php From 733f06e2caeb058d34c224113e18d294d1b66c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 17:46:41 +0100 Subject: [PATCH 06/32] phpunit: use symfony/phpunit-bridge --- .gitignore | 1 + composer.json | 3 ++- phpunit.xml | 27 ++++++++++++++++++++ phpunit.xml.dist | 29 ---------------------- tests/Metrics/Collector/InMemoryTest.php | 2 +- tests/Metrics/Collector/InfluxDBTest.php | 2 +- tests/Metrics/Collector/PrometheusTest.php | 2 +- 7 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 phpunit.xml delete mode 100644 phpunit.xml.dist diff --git a/.gitignore b/.gitignore index c8153b5..50b0964 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /composer.lock /vendor/ +/.phpunit.cache/ diff --git a/composer.json b/composer.json index 95ceaea..1f6860f 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "okitsu/zabbix-sender": "*@dev", "symfony/config": "~3.3 || ~4.0", "symfony/dependency-injection": "~3.3 || ~4.0", - "symfony/http-kernel": "~3.3 || ~4.0" + "symfony/http-kernel": "~3.3 || ~4.0", + "symfony/phpunit-bridge": "^7.0" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..5d58267 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,27 @@ + + + + + tests + + + + + + src + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 2023ce8..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - ./src/Beberlei/Bundle/MetricsBundle/Tests - ./src/Beberlei/Metrics/Tests/ - - - - - - ./src - - ./src/Beberlei/Bundle/MetricsBundle/Tests - ./src/Beberlei/Metrics/Tests/ - - - - diff --git a/tests/Metrics/Collector/InMemoryTest.php b/tests/Metrics/Collector/InMemoryTest.php index 66e1c3f..96116cc 100644 --- a/tests/Metrics/Collector/InMemoryTest.php +++ b/tests/Metrics/Collector/InMemoryTest.php @@ -24,7 +24,7 @@ class InMemoryTest extends TestCase /** @var InMemory */ private $collector; - public function setUp() + protected function setUp(): void { $this->collector = new InMemory(); } diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php index e5c0f40..7efc63d 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDBTest.php @@ -29,7 +29,7 @@ class InfluxDBTest extends TestCase */ private $collector; - protected function setUp() + protected function setUp(): void { $this->client = $this->getMockBuilder('\\InfluxDB\\Client') ->disableOriginalConstructor() diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index f42f3ab..94a25fa 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -32,7 +32,7 @@ class PrometheusTest extends TestCase */ private $collector; - protected function setUp() + protected function setUp(): void { $this->collectorRegistryMock = $this->getMockBuilder('\\Prometheus\\CollectorRegistry') ->disableOriginalConstructor() From 4d33fa1cba688b3f8b939d302496b4d08f6d9eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:30:20 +0100 Subject: [PATCH 07/32] zabbix: Remove collector --- README.md | 21 ----- composer.json | 2 - src/Metrics/Collector/Zabbix.php | 78 ------------------- src/Metrics/Factory.php | 30 ------- .../BeberleiMetricsExtension.php | 15 ---- .../Resources/config/metrics.xml | 4 - tests/Metrics/FactoryTest.php | 7 -- .../BeberleiMetricsExtensionTest.php | 49 ------------ 8 files changed, 206 deletions(-) delete mode 100644 src/Metrics/Collector/Zabbix.php diff --git a/README.md b/README.md index abbee4a..27e3b03 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ Currently supported backends: * Null (Dummy that does nothing) * Prometheus * StatsD -* Zabbix * DogStatsD ## Installation @@ -71,17 +70,6 @@ $collector->flush(); 'foo.beberlei.de', - 'server' => 'localhost', - 'port' => 10051, -)); - -$zabbixConfig = \Beberlei\Metrics\Factory::create('zabbix_file', array( - 'hostname' => 'foo.beberlei.de', - 'file' => '/etc/zabbix/zabbix_agentd.conf' -)); - $librato = \Beberlei\Metrics\Factory::create('librato', array( 'hostname' => 'foo.beberlei.de', 'username' => 'foo', @@ -118,15 +106,6 @@ beberlei_metrics: collectors: foo: type: statsd - bar: - type: zabbix - prefix: foo.beberlei.de - host: localhost - port: 10051 - baz: - type: zabbix_file - prefix: foo.beberlei.de - file: /etc/zabbix/zabbix_agentd.conf librato: type: librato username: foo diff --git a/composer.json b/composer.json index 1f6860f..cab3003 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,6 @@ "doctrine/dbal": "~2.0", "jimdo/prometheus_client_php": "^0.5", "kriswallsmith/buzz": "*", - "okitsu/zabbix-sender": "*@dev", "symfony/config": "~3.3 || ~4.0", "symfony/dependency-injection": "~3.3 || ~4.0", "symfony/http-kernel": "~3.3 || ~4.0", @@ -42,7 +41,6 @@ }, "suggest": { "corley/influxdb-sdk": "For InfluxDB integration", - "okitsu/zabbix-sender": "For zabbix integration", "kriswallsmith/buzz": "For Librato integration", "jimdo/prometheus_client_php": "For Prometheus integration" }, diff --git a/src/Metrics/Collector/Zabbix.php b/src/Metrics/Collector/Zabbix.php deleted file mode 100644 index 214e064..0000000 --- a/src/Metrics/Collector/Zabbix.php +++ /dev/null @@ -1,78 +0,0 @@ -sender = $sender; - $this->prefix = $prefix ?: gethostname(); - } - - /** - * {@inheritdoc} - */ - public function increment($variable) - { - $this->sender->addData($this->prefix, $variable, '1'); - } - - /** - * {@inheritdoc} - */ - public function decrement($variable) - { - $this->sender->addData($this->prefix, $variable, '-1'); - } - - /** - * {@inheritdoc} - */ - public function timing($variable, $time) - { - $this->sender->addData($this->prefix, $variable, $time); - } - - /** - * {@inheritdoc} - */ - public function measure($variable, $value) - { - $this->sender->addData($this->prefix, $variable, $value); - } - - /** - * {@inheritdoc} - */ - public function flush() - { - $this->sender->send(); - } -} diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 0e6ba73..8a3704b 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,8 +13,6 @@ namespace Beberlei\Metrics; -use Net\Zabbix\Sender; -use Net\Zabbix\Agent\Config; use Buzz\Browser; use Buzz\Client\Curl; @@ -108,34 +106,6 @@ public static function create($type, array $options = array()) return new Collector\Graphite($options['host'], $options['port']); - case 'zabbix': - if (!isset($options['hostname'])) { - throw new MetricsException('Hostname is required for zabbix collector.'); - } - - if (!isset($options['server']) && !isset($options['port'])) { - $sender = new Sender(); - } elseif (isset($options['server']) && !isset($options['port'])) { - $sender = new Sender($options['server']); - } elseif (!isset($options['server']) && isset($options['port'])) { - throw new MetricsException('You should specified a server if you specified a port.'); - } else { - $sender = new Sender($options['server'], $options['port']); - } - - return new Collector\Zabbix($sender, $options['hostname']); - - case 'zabbix_file': - if (!isset($options['hostname'])) { - throw new MetricsException('Hostname is required for zabbix collector.'); - } - - $file = isset($options['file']) ? $options['file'] : null; - $sender = new Sender(); - $sender->importAgentConfig(new Config($file)); - - return new Collector\Zabbix($sender, $options['hostname']); - case 'librato': if (!isset($options['hostname'])) { throw new MetricsException('Hostname is required for librato collector.'); diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 3da3fe1..f943c17 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -102,21 +102,6 @@ private function createCollector($type, $config) $definition->replaceArgument(1, $config['port'] ?: 8125); $definition->replaceArgument(2, (string) $config['prefix']); - return $definition; - case 'zabbix': - $sender = new Definition('Net\Zabbix\Sender'); - if ($config['file']) { - $senderConfig = new Definition('Net\Zabbix\Agent\Config'); - $senderConfig->addArgument($config['file']); - $sender->addMethodCall('importAgentConfig', array($senderConfig)); - } else { - $sender->addArgument($config['host'] ?: 'localhost'); - $sender->addArgument((int) $config['port'] ?: 10051); - } - - $definition->replaceArgument(0, $sender); - $definition->replaceArgument(1, $config['prefix']); - return $definition; default: throw new \InvalidArgumentException(sprintf('The type "%s" is not supported', $type)); diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index 128fd63..5cae232 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -56,10 +56,6 @@ - - - - diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index fa7cd1a..4fa931c 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -21,9 +21,6 @@ public function getCreateValidMetricTests() array('Beberlei\Metrics\Collector\DogStatsD', 'dogstatsd', array('host' => 'localhost')), array('Beberlei\Metrics\Collector\Graphite', 'graphite'), array('Beberlei\Metrics\Collector\Graphite', 'graphite', array('host' => 'localhost', 'port' => 1234)), - array('Beberlei\Metrics\Collector\Zabbix', 'zabbix', array('hostname' => 'foobar.com', 'server' => 'localhost', 'port' => 1234)), - array('Beberlei\Metrics\Collector\Zabbix', 'zabbix_file', array('hostname' => 'foobar.com')), - array('Beberlei\Metrics\Collector\Zabbix', 'zabbix_file', array('hostname' => 'foobar.com', 'file' => '/tmp/foobar')), array('Beberlei\Metrics\Collector\Librato', 'librato', array('hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password')), array('Beberlei\Metrics\Collector\DoctrineDBAL', 'doctrine_dbal', array('connection' => $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock())), array('Beberlei\Metrics\Collector\Logger', 'logger', array('logger' => new NullLogger())), @@ -55,10 +52,6 @@ public function getCreateThrowExceptionIfOptionsAreInvalidTests() array('You should specified a host and a port if you specified a prefix.', 'dogstatsd', array('port' => '1234', 'prefix' => 'prefix')), array('You should specified a host and a port if you specified a prefix.', 'dogstatsd', array('hostname' => 'foobar.com', 'prefix' => 'prefix')), array('You should specified a host if you specified a port.', 'graphite', array('port' => '1234')), - array('Hostname is required for zabbix collector.', 'zabbix'), - array('Hostname is required for zabbix collector.', 'zabbix', array('hostname', 'foobar.com')), - array('You should specified a server if you specified a port.', 'zabbix', array('hostname' => 'foobar.com', 'port' => '1234')), - array('Hostname is required for zabbix collector.', 'zabbix_file'), array('Hostname is required for librato collector.', 'librato'), array('No username given for librato collector.', 'librato', array('hostname' => 'foobar.com')), array('No password given for librato collector.', 'librato', array('hostname' => 'foobar.com', 'username' => 'username')), diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index 5fdee8d..a301784 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -223,55 +223,6 @@ public function testWithTelegraf() $this->assertEquals(',string_tag=first_value,int_tag=123', $this->getProperty($collector, 'tags')); } - public function testWithZabbix() - { - $container = $this->createContainer(array( - 'default' => 'simple', - 'collectors' => array( - 'simple' => array( - 'type' => 'zabbix', - ), - 'full' => array( - 'type' => 'zabbix', - 'prefix' => 'foo.beberlei.de', - 'host' => 'zabbix.localhost', - 'port' => 1234, - ), - 'file' => array( - 'type' => 'zabbix', - 'prefix' => 'foo.beberlei.de', - 'file' => '/etc/zabbix/zabbix_agentd.conf', - ), - ), - ), array( - 'beberlei_metrics.collector.simple', - 'beberlei_metrics.collector.full', - 'beberlei_metrics.collector.file' - )); - - $collector = $container->get('beberlei_metrics.collector.simple'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Zabbix', $collector); - $this->assertSame(gethostname(), $this->getProperty($collector, 'prefix')); - $sender = $this->getProperty($collector, 'sender'); - $this->assertInstanceOf('Net\Zabbix\Sender', $sender); - $this->assertSame('localhost', $this->getProperty($sender, '_servername')); - $this->assertSame(10051, $this->getProperty($sender, '_serverport')); - - $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Zabbix', $collector); - $this->assertSame('foo.beberlei.de', $this->getProperty($collector, 'prefix')); - $sender = $this->getProperty($collector, 'sender'); - $this->assertInstanceOf('Net\Zabbix\Sender', $sender); - $this->assertSame('zabbix.localhost', $this->getProperty($sender, '_servername')); - $this->assertSame(1234, $this->getProperty($sender, '_serverport')); - - $collector = $container->get('beberlei_metrics.collector.file'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Zabbix', $collector); - $this->assertSame('foo.beberlei.de', $this->getProperty($collector, 'prefix')); - $sender = $this->getProperty($collector, 'sender'); - $this->assertInstanceOf('Net\Zabbix\Sender', $sender); - } - public function testWithInfluxDB() { $influxDBClientMock = $this->getMockBuilder('InfluxDB\Client') From 1cdf63d03b1661a1fbd29cb5c7333ebfbf09d8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:32:20 +0100 Subject: [PATCH 08/32] composer: bump requirements to PHP 8.1+ --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index cab3003..b26f381 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "psr/log": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { + "php": ">=8.1", "corley/influxdb-sdk": "^0.5.1", "doctrine/dbal": "~2.0", "jimdo/prometheus_client_php": "^0.5", From 5457cf6dcb70c5d0350d8d8df01e6621b5e74149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:34:49 +0100 Subject: [PATCH 09/32] composer: drop support for Symfony < 5.4 --- composer.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index b26f381..b361c37 100644 --- a/composer.json +++ b/composer.json @@ -24,10 +24,15 @@ "doctrine/dbal": "~2.0", "jimdo/prometheus_client_php": "^0.5", "kriswallsmith/buzz": "*", - "symfony/config": "~3.3 || ~4.0", - "symfony/dependency-injection": "~3.3 || ~4.0", - "symfony/http-kernel": "~3.3 || ~4.0", - "symfony/phpunit-bridge": "^7.0" + "symfony/config": "^5.4 || ^6.4 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", + "symfony/phpunit-bridge": "^6.4 || ^7.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4" }, "autoload": { "psr-4": { From b5ac1c9ad8ec526074d2a3f956c5d1c360cc2e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:41:55 +0100 Subject: [PATCH 10/32] composer: switch to symfony http-client instead of buzz --- composer.json | 3 ++- src/Metrics/Collector/Librato.php | 21 +++++++++---------- src/Metrics/Factory.php | 17 +++++---------- .../Resources/config/metrics.xml | 9 +++----- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index b361c37..83e700f 100644 --- a/composer.json +++ b/composer.json @@ -23,15 +23,16 @@ "corley/influxdb-sdk": "^0.5.1", "doctrine/dbal": "~2.0", "jimdo/prometheus_client_php": "^0.5", - "kriswallsmith/buzz": "*", "symfony/config": "^5.4 || ^6.4 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", + "symfony/http-client": "^5.4 || ^6.4 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", "symfony/phpunit-bridge": "^6.4 || ^7.0" }, "conflict": { "symfony/config": "<5.4", "symfony/dependency-injection": "<5.4", + "symfony/http-client": "<5.4", "symfony/http-kernel": "<5.4" }, "autoload": { diff --git a/src/Metrics/Collector/Librato.php b/src/Metrics/Collector/Librato.php index 9bbd96d..b53c9e5 100644 --- a/src/Metrics/Collector/Librato.php +++ b/src/Metrics/Collector/Librato.php @@ -13,12 +13,12 @@ namespace Beberlei\Metrics\Collector; -use Buzz\Browser; +use Symfony\Contracts\HttpClient\Exception\ExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; class Librato implements Collector { - /** @var \Buzz\Browser */ - private $browser; + private HttpClientInterface $httpClient; /** @var string */ private $source; @@ -36,14 +36,13 @@ class Librato implements Collector ); /** - * @param \Buzz\Browser $browser * @param string $source * @param string $username * @param string $password */ - public function __construct(Browser $browser, $source, $username, $password) + public function __construct(HttpClientInterface $httpClient, $source, $username, $password) { - $this->browser = $browser; + $this->httpClient = $httpClient; $this->source = $source; $this->username = $username; $this->password = $password; @@ -107,12 +106,12 @@ public function flush() } try { - $this->browser->post('https://metrics-api.librato.com/v1/metrics', array( - 'Authorization: Basic '.base64_encode($this->username.':'.$this->password), - 'Content-Type: application/json', - ), json_encode($this->data)); + $this->httpClient->request('POST', 'https://metrics-api.librato.com/v1/metrics', [ + 'auth_basic' => [$this->username, $this->password], + 'json' => $this->data, + ]); $this->data = array('gauges' => array(), 'counters' => array()); - } catch (\Exception $e) { + } catch (ExceptionInterface) { } } } diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 8a3704b..01ab153 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,18 +13,15 @@ namespace Beberlei\Metrics; -use Buzz\Browser; -use Buzz\Client\Curl; +use Symfony\Component\HttpClient\HttpClient; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * Static factory for Metrics Collectors. */ abstract class Factory { - /** - * @var Buzz\Browser - */ - private static $httpClient; + private static HttpClientInterface $httpClient; /** * Create Metrics Collector Instance. @@ -162,12 +159,8 @@ public static function create($type, array $options = array()) } } - private static function getHttpClient() + private static function getHttpClient(): HttpClientInterface { - if (self::$httpClient === null) { - self::$httpClient = new Browser(new Curl()); - } - - return self::$httpClient; + return self::$httpClient ??= HttpClient::create(); } } diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index 5cae232..0d53ed4 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -6,11 +6,8 @@ - - - - - + + @@ -26,7 +23,7 @@ - + From 11f8e4a0c79dd7132c33e1f21e95ab91d985a7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:48:49 +0100 Subject: [PATCH 11/32] chore: modernise PHP code --- .../{Collector.php => CollectorInterface.php} | 23 +- src/Metrics/Collector/DoctrineDBAL.php | 56 +--- src/Metrics/Collector/DogStatsD.php | 71 +--- ...or.php => GaugeableCollectorInterface.php} | 7 +- src/Metrics/Collector/Graphite.php | 69 ++-- src/Metrics/Collector/InMemory.php | 119 ++----- src/Metrics/Collector/InfluxDB.php | 71 ++-- .../InlineTaggableGaugeableCollector.php | 65 ---- .../InlineTaggableGaugeableNullCollector.php | 64 ---- src/Metrics/Collector/Librato.php | 87 ++--- src/Metrics/Collector/Logger.php | 48 +-- src/Metrics/Collector/NullCollector.php | 36 +-- src/Metrics/Collector/Prometheus.php | 95 ++---- src/Metrics/Collector/StatsD.php | 63 +--- src/Metrics/Collector/TaggableCollector.php | 27 -- ...ull.php => TaggableCollectorInterface.php} | 6 +- src/Metrics/Collector/Telegraf.php | 94 ++---- src/Metrics/Factory.php | 76 +++-- .../BeberleiMetricsExtension.php | 21 +- .../DependencyInjection/Configuration.php | 18 +- tests/Metrics/Collector/InMemoryTest.php | 26 +- tests/Metrics/Collector/InfluxDBTest.php | 75 +---- tests/Metrics/Collector/PrometheusTest.php | 103 +++--- tests/Metrics/FactoryTest.php | 68 ++-- .../BeberleiMetricsExtensionTest.php | 304 +++++------------- 25 files changed, 440 insertions(+), 1252 deletions(-) rename src/Metrics/Collector/{Collector.php => CollectorInterface.php} (61%) rename src/Metrics/Collector/{GaugeableCollector.php => GaugeableCollectorInterface.php} (76%) delete mode 100644 src/Metrics/Collector/InlineTaggableGaugeableCollector.php delete mode 100644 src/Metrics/Collector/InlineTaggableGaugeableNullCollector.php delete mode 100644 src/Metrics/Collector/TaggableCollector.php rename src/Metrics/Collector/{Null.php => TaggableCollectorInterface.php} (79%) diff --git a/src/Metrics/Collector/Collector.php b/src/Metrics/Collector/CollectorInterface.php similarity index 61% rename from src/Metrics/Collector/Collector.php rename to src/Metrics/Collector/CollectorInterface.php index 51d44a0..5431a87 100644 --- a/src/Metrics/Collector/Collector.php +++ b/src/Metrics/Collector/CollectorInterface.php @@ -13,43 +13,32 @@ namespace Beberlei\Metrics\Collector; -/** - * Collector interface. - */ -interface Collector +interface CollectorInterface { /** * Updates a counter by some arbitrary amount. - * - * @param string $variable - * @param int $value The amount to increment the counter by */ - public function measure($variable, $value); + public function measure(string $variable, int $value, array $tags = []): void; /** * Increments a counter. - * - * @param string $variable */ - public function increment($variable); + public function increment(string $variable, array $tags = []): void; /** * Decrements a counter. - * - * @param string $variable */ - public function decrement($variable); + public function decrement(string $variable, array $tags = []): void; /** * Records a timing. * - * @param string $variable * @param int $time The duration of the timing in milliseconds */ - public function timing($variable, $time); + public function timing(string $variable, int $time, array $tags = []): void; /** * Sends the metrics to the adapter backend. */ - public function flush(); + public function flush(): void; } diff --git a/src/Metrics/Collector/DoctrineDBAL.php b/src/Metrics/Collector/DoctrineDBAL.php index 33ac0f6..caf1b47 100644 --- a/src/Metrics/Collector/DoctrineDBAL.php +++ b/src/Metrics/Collector/DoctrineDBAL.php @@ -29,58 +29,36 @@ * The Primary key can either be a surrogate (id) or * has to span all 3 columns. */ -class DoctrineDBAL implements Collector +class DoctrineDBAL implements CollectorInterface { - /** @var \Doctrine\DBAL\Connection */ - private $conn; + private array $data = []; - /** @var array */ - private $data; - - /** - * @param \Doctrine\DBAL\Connection $conn - */ - public function __construct(Connection $conn) - { - $this->conn = $conn; + public function __construct( + private readonly Connection $conn + ) { } - /** - * {@inheritdoc} - */ - public function timing($stat, $time) + public function measure(string $variable, int $value, array $tags = []): void { - $this->data[] = array($stat, $time, date('Y-m-d')); + $this->data[] = [$variable, $value, date('Y-m-d')]; } - /** - * {@inheritdoc} - */ - public function increment($stats) + public function increment(string $variable, array $tags = []): void { - $this->data[] = array($stats, 1, date('Y-m-d')); + $this->data[] = [$variable, 1, date('Y-m-d')]; } - /** - * {@inheritdoc} - */ - public function decrement($stats) + public function decrement(string $variable, array $tags = []): void { - $this->data[] = array($stats, -1, date('Y-m-d')); + $this->data[] = [$variable, -1, date('Y-m-d')]; } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function timing(string $variable, int $time, array $tags = []): void { - $this->data[] = array($variable, $value, date('Y-m-d')); + $this->data[] = [$variable, $time, date('Y-m-d')]; } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data) { return; @@ -95,14 +73,14 @@ public function flush() $stmt->bindParam(1, $measurement[0]); $stmt->bindParam(2, $measurement[1]); $stmt->bindParam(3, $measurement[2]); - $stmt->execute(); + $stmt->executeStatement(); } $this->conn->commit(); - } catch (Exception $e) { + } catch (Exception) { $this->conn->rollback(); } - $this->data = array(); + $this->data = []; } } diff --git a/src/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php index 7f9a82d..9fdbbdd 100644 --- a/src/Metrics/Collector/DogStatsD.php +++ b/src/Metrics/Collector/DogStatsD.php @@ -13,77 +13,43 @@ namespace Beberlei\Metrics\Collector; -class DogStatsD implements Collector, InlineTaggableGaugeableCollector +class DogStatsD implements CollectorInterface, GaugeableCollectorInterface { - /** @var string */ - private $host; + private array $data = []; - /** @var string */ - private $port; - - /** @var string */ - private $prefix; - - /** @var array */ - private $data; - - /** - * @param string $host - * @param string $port - * @param string $prefix - */ - public function __construct($host = 'localhost', $port = '8125', $prefix = '') - { - $this->host = $host; - $this->port = $port; - $this->prefix = $prefix; - $this->data = array(); + public function __construct( + private readonly string $host = 'localhost', + private readonly int $port = 8125, + private readonly string $prefix = '', + ) { } - /** - * {@inheritdoc} - */ - public function measure($variable, $value, $tags = array()) + public function measure(string $variable, int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|c%s', $variable, $value, $this->buildTagString($tags)); } - /** - * {@inheritdoc} - */ - public function increment($variable, $tags = array()) + public function increment(string $variable, array $tags = []): void { $this->data[] = $variable.':1|c'.$this->buildTagString($tags); } - /** - * {@inheritdoc} - */ - public function decrement($variable, $tags = array()) + public function decrement(string $variable, array $tags = []): void { $this->data[] = $variable.':-1|c'.$this->buildTagString($tags); } - /** - * {@inheritdoc} - */ - public function timing($variable, $time, $tags = array()) + public function timing(string $variable, int $time, array $tags = []): void { $this->data[] = sprintf('%s:%s|ms%s', $variable, $time, $this->buildTagString($tags)); } - /** - * {@inheritdoc} - */ - public function gauge($variable, $value, $tags = array()) + public function gauge(string $variable, int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|g%s', $variable, $value, $this->buildTagString($tags)); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data) { return; @@ -99,24 +65,21 @@ public function flush() foreach ($this->data as $line) { fwrite($fp, $this->prefix.$line); } + error_reporting($level); fclose($fp); - $this->data = array(); + $this->data = []; } /** * Given a key/value map of metric tags, builds them into a * DogStatsD tag string and returns the string. - * - * @param $tags array - * - * @return string */ - private function buildTagString($tags) + private function buildTagString(array $tags): string { - $results = array(); + $results = []; foreach ($tags as $key => $value) { $results[] = sprintf('%s:%s', $key, $value); diff --git a/src/Metrics/Collector/GaugeableCollector.php b/src/Metrics/Collector/GaugeableCollectorInterface.php similarity index 76% rename from src/Metrics/Collector/GaugeableCollector.php rename to src/Metrics/Collector/GaugeableCollectorInterface.php index ffeb98f..56938f9 100644 --- a/src/Metrics/Collector/GaugeableCollector.php +++ b/src/Metrics/Collector/GaugeableCollectorInterface.php @@ -13,13 +13,10 @@ namespace Beberlei\Metrics\Collector; -interface GaugeableCollector +interface GaugeableCollectorInterface { /** * Updates a gauge by an arbitrary amount. - * - * @param string $variable - * @param int $value */ - public function gauge($variable, $value); + public function gauge(string $variable, int $value, array $tags = []): void; } diff --git a/src/Metrics/Collector/Graphite.php b/src/Metrics/Collector/Graphite.php index e052565..bdd9b4a 100644 --- a/src/Metrics/Collector/Graphite.php +++ b/src/Metrics/Collector/Graphite.php @@ -18,68 +18,39 @@ /** * Sends statistics to the stats daemon over UDP or TCP. */ -class Graphite implements Collector +class Graphite implements CollectorInterface { - /** @var string */ - private $protocol; + private array $data = []; - /** @var string */ - private $host; - - /** @var int */ - private $port; - - /** @var array */ - private $data = array(); - - /** - * @param string $host - * @param int $port - * @param string $protocol - */ - public function __construct($host = 'localhost', $port = 2003, $protocol = 'tcp') - { - $this->host = $host; - $this->port = $port; - $this->protocol = $protocol; + public function __construct( + private readonly string $host = 'localhost', + private readonly int $port = 2003, + private readonly string $protocol = 'tcp' + ) { } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function measure(string $variable, int $value, array $tags = []): void { - $this->push($variable, $time); + $this->push($variable, $value); } - /** - * {@inheritdoc} - */ - public function increment($variable) + + public function increment(string $variable, array $tags = []): void { $this->push($variable, 1); } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { $this->push($variable, -1); } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function timing(string $variable, int $time, array $tags = []): void { - $this->push($variable, $value); + $this->push($variable, $time); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data) { return; @@ -93,21 +64,21 @@ public function flush() } foreach ($this->data as $line) { - fwrite($fp, $line); + fwrite($fp, (string) $line); } fclose($fp); - } catch (Exception $e) { + } catch (Exception) { } - $this->data = array(); + $this->data = []; } - public function push($stat, $value, $time = null) + public function push(string $variable, int|float $value, ?int $time = null): void { $this->data[] = sprintf( is_float($value) ? "%s %.18f %d\n" : "%s %d %d\n", - $stat, + $variable, $value, $time ?: time() ); diff --git a/src/Metrics/Collector/InMemory.php b/src/Metrics/Collector/InMemory.php index 95df43b..bae39f3 100644 --- a/src/Metrics/Collector/InMemory.php +++ b/src/Metrics/Collector/InMemory.php @@ -17,138 +17,67 @@ * Stores metrics in memory. * Useful for testing and with any custom persistence mechanisms. */ -class InMemory implements Collector, GaugeableCollector +class InMemory implements CollectorInterface, GaugeableCollectorInterface { /** @var int[] */ - private $incrementData = []; + private array $incrementData = []; + /** @var int[] */ - private $gaugeData = []; + private array $gaugeData = []; + /** @var int[] */ - private $timingData = []; + private array $timingData = []; - /** - * Updates a counter by some arbitrary amount. - * - * @param string $variable - * @param int $value The amount to increment the counter by - */ - public function measure($variable, $value) + public function measure(string $variable, int $value, array $tags = []): void { - if (!isset($this->incrementData[$variable])) { - $this->incrementData[$variable] = 0; - } + $this->incrementData[$variable] ??= 0; $this->incrementData[$variable] += $value; } - /** - * Increments a counter. - * - * @param string $variable - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { $this->measure($variable, 1); } - /** - * Decrements a counter. - * - * @param string $variable - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { $this->measure($variable, -1); } - /** - * Records a timing. - * - * @param string $variable - * @param int $time The duration of the timing in milliseconds - */ - public function timing($variable, $time) + public function timing(string $variable, int $time, array $tags = []): void { if (!isset($this->timingData[$variable])) { $this->timingData[$variable] = 0; } - $this->timingData[$variable] = $time; - } - /** - * Sends the metrics to the adapter backend. - */ - public function flush() - { - $this->timingData = []; - $this->gaugeData = []; - $this->incrementData = []; + $this->timingData[$variable] = $time; } - /** - * Updates a gauge by an arbitrary amount. - * - * @param string $variable - * @param int $value - */ - public function gauge($variable, $value) + public function gauge(string $variable, int $value, array $tags = []): void { - $sign = substr($value, 0, 1); - - if (in_array($sign, ['-', '+'])) { - $this->gaugeIncrement($variable, (int) $value); - - return; - } - $this->gaugeData[$variable] = $value; } - /** - * Returns current value of incremented/decremented/measured variable. - * - * @param string $variable - * - * @return int - */ - public function getMeasure($variable) + public function flush(): void { - return isset($this->incrementData[$variable]) ? $this->incrementData[$variable] : 0; + $this->timingData = []; + $this->gaugeData = []; + $this->incrementData = []; } - /** - * Returns current value of gauged variable. - * - * @param string $variable - * - * @return int - */ - public function getGauge($variable) + + public function getMeasure(string $variable): int { - return isset($this->gaugeData[$variable]) ? $this->gaugeData[$variable] : 0; + return $this->incrementData[$variable] ?? 0; } - /** - * Returns current value of timed variable. - * - * @param string $variable - * - * @return int - */ - public function getTiming($variable) + public function getGauge(string $variable): int { - return isset($this->timingData[$variable]) ? $this->timingData[$variable] : 0; + return $this->gaugeData[$variable] ?? 0; } - /** - * @param string $variable - * @param int $value - */ - private function gaugeIncrement($variable, $value) + public function getTiming(string $variable): int { - if (!isset($this->gaugeData[$variable])) { - $this->gaugeData[$variable] = 0; - } - - $this->gaugeData[$variable] += $value; + return $this->timingData[$variable] ?? 0; } } diff --git a/src/Metrics/Collector/InfluxDB.php b/src/Metrics/Collector/InfluxDB.php index ae657b8..e995972 100644 --- a/src/Metrics/Collector/InfluxDB.php +++ b/src/Metrics/Collector/InfluxDB.php @@ -15,81 +15,46 @@ use InfluxDB\Client; -class InfluxDB implements Collector, TaggableCollector +class InfluxDB implements CollectorInterface, TaggableCollectorInterface { - /** @var \InfluxDB\Client */ - private $client; + private array $data = []; - /** @var array */ - private $data = array(); - - /** @var array */ - private $tags = array(); - - /** - * @param Client $client - */ - public function __construct(Client $client) - { - $this->client = $client; + public function __construct( + private readonly Client $client, + private array $tags = [], + ) { } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function measure(string $variable, int $value, array $tags = []): void { - $this->data[] = array($variable, 1); + $this->data[] = [$variable, $value, $tags]; } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function increment(string $variable, array $tags = []): void { - $this->data[] = array($variable, -1); + $this->data[] = [$variable, 1, $tags]; } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function decrement(string $variable, array $tags = []): void { - $this->data[] = array($variable, $time); + $this->data[] = [$variable, -1, $tags]; } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function timing(string $variable, int $time, array $tags = []): void { - $this->data[] = array($variable, $value); + $this->data[] = [$variable, $time, $tags]; } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { foreach ($this->data as $data) { - $this->client->mark(array( - 'points' => array( - array( - 'measurement' => $data[0], - 'fields' => array('value' => $data[1]), - ), - ), - 'tags' => $this->tags, - )); + $this->client->mark(['points' => [['measurement' => $data[0], 'fields' => ['value' => $data[1]]]], 'tags' => $data[2] + $this->tags]); } - $this->data = array(); + $this->data = []; } - /** - * {@inheritdoc} - */ - public function setTags($tags) + public function setTags(array $tags): void { $this->tags = $tags; } diff --git a/src/Metrics/Collector/InlineTaggableGaugeableCollector.php b/src/Metrics/Collector/InlineTaggableGaugeableCollector.php deleted file mode 100644 index 1fdebb1..0000000 --- a/src/Metrics/Collector/InlineTaggableGaugeableCollector.php +++ /dev/null @@ -1,65 +0,0 @@ - [], 'gauges' => []]; - /** @var string */ - private $source; - - /** @var string */ - private $username; - - /** @var string */ - private $password; - - /** @var array */ - private $data = array( - 'counters' => array(), - 'gauges' => array(), - ); - - /** - * @param string $source - * @param string $username - * @param string $password - */ - public function __construct(HttpClientInterface $httpClient, $source, $username, $password) - { - $this->httpClient = $httpClient; - $this->source = $source; - $this->username = $username; - $this->password = $password; + public function __construct( + private readonly HttpClientInterface $httpClient, + private readonly string $source, + private readonly string $username, + private readonly string $password + ) { } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function measure(string $variable, int $value, array $tags = []): void { - $this->data['counters'][] = array( - 'source' => $this->source, - 'name' => $variable, - 'value' => 1, - ); + $this->data['gauges'][] = ['source' => $this->source, 'name' => $variable, 'value' => $value]; } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function increment(string $variable, array $tags = []): void { - $this->data['counters'][] = array( - 'source' => $this->source, - 'name' => $variable, - 'value' => -1, - ); + $this->data['counters'][] = ['source' => $this->source, 'name' => $variable, 'value' => 1]; } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function decrement(string $variable, array $tags = []): void { - $this->data['gauges'][] = array( - 'source' => $this->source, - 'name' => $variable, - 'value' => $time, - ); + $this->data['counters'][] = ['source' => $this->source, 'name' => $variable, 'value' => -1]; } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function timing(string $variable, int $time, array $tags = []): void { - $this->data['gauges'][] = array( - 'source' => $this->source, - 'name' => $variable, - 'value' => $value, - ); + $this->data['gauges'][] = ['source' => $this->source, 'name' => $variable, 'value' => $time]; } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data['gauges'] && !$this->data['counters']) { return; @@ -110,7 +59,7 @@ public function flush() 'auth_basic' => [$this->username, $this->password], 'json' => $this->data, ]); - $this->data = array('gauges' => array(), 'counters' => array()); + $this->data = ['gauges' => [], 'counters' => []]; } catch (ExceptionInterface) { } } diff --git a/src/Metrics/Collector/Logger.php b/src/Metrics/Collector/Logger.php index c501981..0a294d8 100644 --- a/src/Metrics/Collector/Logger.php +++ b/src/Metrics/Collector/Logger.php @@ -15,63 +15,39 @@ use Psr\Log\LoggerInterface; -class Logger implements Collector, GaugeableCollector +class Logger implements CollectorInterface, GaugeableCollectorInterface { - /** @var \Psr\Log\LoggerInterface */ - private $logger; + public function __construct( + private readonly LoggerInterface $logger, + ) { + } - /** - * @param \Psr\Log\LoggerInterface $logger - */ - public function __construct(LoggerInterface $logger) + public function measure(string $variable, int $value, array $tags = []): void { - $this->logger = $logger; + $this->logger->debug(sprintf('measure:%s:%s', $variable, $value)); } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { $this->logger->debug('increment:'.$variable); } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { $this->logger->debug('decrement:'.$variable); } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function timing(string $variable, int $time, array $tags = []): void { $this->logger->debug(sprintf('timing:%s:%s', $variable, $time)); } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) - { - $this->logger->debug(sprintf('measure:%s:%s', $variable, $value)); - } - - /** - * {@inheritdoc} - */ - public function gauge($variable, $value) + public function gauge(string $variable, int $value, array $tags = []): void { $this->logger->debug(sprintf('gauge:%s:%s', $variable, $value)); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { $this->logger->debug('flush'); } diff --git a/src/Metrics/Collector/NullCollector.php b/src/Metrics/Collector/NullCollector.php index acb543b..38f05c1 100644 --- a/src/Metrics/Collector/NullCollector.php +++ b/src/Metrics/Collector/NullCollector.php @@ -13,47 +13,33 @@ namespace Beberlei\Metrics\Collector; -class NullCollector implements Collector, GaugeableCollector +class NullCollector implements CollectorInterface, GaugeableCollectorInterface, TaggableCollectorInterface { - /** - * {@inheritdoc} - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function timing(string $variable, int $time, array $tags = []): void { } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function measure(string $variable, int $value, array $tags = []): void { } - /** - * {@inheritdoc} - */ - public function gauge($variable, $value) + public function gauge(string $variable, int $value, array $tags = []): void { } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void + { + } + + public function setTags(array $tags): void { } } diff --git a/src/Metrics/Collector/Prometheus.php b/src/Metrics/Collector/Prometheus.php index ced78e2..6945adf 100644 --- a/src/Metrics/Collector/Prometheus.php +++ b/src/Metrics/Collector/Prometheus.php @@ -13,87 +13,42 @@ namespace Beberlei\Metrics\Collector; +use Prometheus\Gauge; use Prometheus\CollectorRegistry; use Prometheus\Exception\MetricNotFoundException; -class Prometheus implements Collector, TaggableCollector +class Prometheus implements CollectorInterface, TaggableCollectorInterface { - /** - * @var CollectorRegistry - */ - private $collectorRegistry; - - /** - * @var string - */ - private $namespace = ''; - - /** @var array */ - private $data = array( - 'counters' => array(), - 'gauges' => array(), - ); - - /** - * @var array - */ - private $tags = array(); - - /** - * @param CollectorRegistry $collectorRegistry - * @param string $namespace - */ - public function __construct(CollectorRegistry $collectorRegistry, $namespace = '') - { - $this->collectorRegistry = $collectorRegistry; - $this->namespace = $namespace; + private array $data = ['counters' => [], 'gauges' => []]; + + public function __construct( + private readonly CollectorRegistry $collectorRegistry, + private readonly string $namespace = '', + private array $tags = [], + ) { } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function measure(string $variable, int $value, array $tags = []): void { - $this->data['gauges'][] = array( - 'name' => $variable, - 'value' => $value, - ); + $this->data['gauges'][] = ['name' => $variable, 'value' => $value]; } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { - $this->data['counters'][] = array( - 'name' => $variable, - 'value' => 1, - ); + $this->data['counters'][] = ['name' => $variable, 'value' => 1]; } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { - $this->data['counters'][] = array( - 'name' => $variable, - 'value' => -1, - ); + $this->data['counters'][] = ['name' => $variable, 'value' => -1]; } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function timing(string $variable, int $time, array $tags = []): void { $this->measure($variable, $time); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data['gauges'] && !$this->data['counters']) { return; @@ -117,27 +72,19 @@ public function flush() $gauge->set($gaugeData['value'], $tagsValues); } - $this->data = array('counters' => array(), 'gauges' => array()); + $this->data = ['counters' => [], 'gauges' => []]; } - /** - * {@inheritdoc} - */ - public function setTags($tags) + public function setTags(array $tags): void { $this->tags = $tags; } - /** - * @param string $variable - * - * @return \Prometheus\Gauge - */ - private function getOrRegisterGaugeForVariable($variable) + private function getOrRegisterGaugeForVariable(string $variable): Gauge { try { $gauge = $this->collectorRegistry->getGauge($this->namespace, $variable); - } catch (MetricNotFoundException $e) { + } catch (MetricNotFoundException) { $gauge = $this->collectorRegistry->registerGauge( $this->namespace, $variable, diff --git a/src/Metrics/Collector/StatsD.php b/src/Metrics/Collector/StatsD.php index e6c92fc..4ae2fc8 100644 --- a/src/Metrics/Collector/StatsD.php +++ b/src/Metrics/Collector/StatsD.php @@ -16,77 +16,43 @@ /** * Sends statistics to the stats daemon over UDP. */ -class StatsD implements Collector, GaugeableCollector +class StatsD implements CollectorInterface, GaugeableCollectorInterface { - /** @var string */ - private $host; + private array $data = []; - /** @var string */ - private $port; - - /** @var string */ - private $prefix; - - /** @var array */ - private $data; - - /** - * @param string $host - * @param string $port - * @param string $prefix - */ - public function __construct($host = 'localhost', $port = '8125', $prefix = '') - { - $this->host = $host; - $this->port = $port; - $this->prefix = $prefix; - $this->data = array(); + public function __construct( + private readonly string $host = 'localhost', + private readonly int $port = 8125, + private readonly string $prefix = '', + ) { } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function timing(string $variable, int $time, array $tags = []): void { $this->data[] = sprintf('%s:%s|ms', $variable, $time); } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { $this->data[] = $variable.':1|c'; } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { $this->data[] = $variable.':-1|c'; } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function measure(string $variable, int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|c', $variable, $value); } - /** - * {@inheritdoc} - */ - public function gauge($variable, $value) + public function gauge(string $variable, int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|g', $variable, $value); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data) { return; @@ -102,10 +68,11 @@ public function flush() foreach ($this->data as $line) { fwrite($fp, $this->prefix.$line); } + error_reporting($level); fclose($fp); - $this->data = array(); + $this->data = []; } } diff --git a/src/Metrics/Collector/TaggableCollector.php b/src/Metrics/Collector/TaggableCollector.php deleted file mode 100644 index 2e9cda0..0000000 --- a/src/Metrics/Collector/TaggableCollector.php +++ /dev/null @@ -1,27 +0,0 @@ -host = $host; - $this->port = $port; - $this->prefix = $prefix; - $this->data = array(); - } - - /** - * {@inheritdoc} - */ - public function setTags($tags) - { - $this->tags = http_build_query($tags, '', ','); - $this->tags = (strlen($this->tags) > 0 ? ','.$this->tags : $this->tags); + private array $data = []; + + public function __construct( + private readonly string $host = 'localhost', + private readonly int $port = 8125, + private readonly string $prefix = '', + private string $tags = '', + ) { } - /** - * {@inheritdoc} - */ - public function timing($variable, $time) + public function measure(string $variable, int $value, array $tags = []): void { - $this->data[] = sprintf('%s%s:%s|ms', $variable, $this->tags, $time); + $this->data[] = sprintf('%s%s:%s|c', $variable, $this->tags, $value); } - /** - * {@inheritdoc} - */ - public function increment($variable) + public function increment(string $variable, array $tags = []): void { $this->data[] = $variable.$this->tags.':1|c'; } - /** - * {@inheritdoc} - */ - public function decrement($variable) + public function decrement(string $variable, array $tags = []): void { $this->data[] = $variable.$this->tags.':-1|c'; } - /** - * {@inheritdoc} - */ - public function measure($variable, $value) + public function timing(string $variable, int $time, array $tags = []): void { - $this->data[] = sprintf('%s%s:%s|c', $variable, $this->tags, $value); + $this->data[] = sprintf('%s%s:%s|ms', $variable, $this->tags, $time); } - /** - * {@inheritdoc} - */ - public function gauge($variable, $value) + public function gauge(string $variable, int $value, array $tags = []): void { $this->data[] = sprintf('%s%s:%s|g', $variable, $this->tags, $value); } - /** - * @param $variable - * @param $value - */ - public function set($variable, $value) + public function set(string $variable, string $value): void { $this->data[] = sprintf('%s%s:%s|s', $variable, $this->tags, $value); } - /** - * {@inheritdoc} - */ - public function flush() + public function flush(): void { if (!$this->data) { return; @@ -125,10 +76,17 @@ public function flush() foreach ($this->data as $line) { fwrite($fp, $this->prefix.$line); } + error_reporting($level); fclose($fp); - $this->data = array(); + $this->data = []; + } + + public function setTags(array $tags): void + { + $this->tags = http_build_query($tags, '', ','); + $this->tags = strlen($this->tags) > 0 ? ','.$this->tags : $this->tags; } } diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 01ab153..3d2a012 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,6 +13,17 @@ namespace Beberlei\Metrics; +use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\DogStatsD; +use Beberlei\Metrics\Collector\Telegraf; +use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\Librato; +use Beberlei\Metrics\Collector\DoctrineDBAL; +use Beberlei\Metrics\Collector\Logger; +use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\NullCollector; +use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; +use Beberlei\Metrics\Collector\Prometheus; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -27,81 +38,90 @@ abstract class Factory * Create Metrics Collector Instance. * * @param string $type - * @param array $options * * @throws MetricsException - * * @return Collector\Collector */ - public static function create($type, array $options = array()) + public static function create($type, array $options = []) { switch ($type) { case 'statsd': if ((!isset($options['host']) || !isset($options['port'])) && isset($options['prefix'])) { throw new MetricsException('You should specified a host and a port if you specified a prefix.'); } + if (!isset($options['host']) && !isset($options['port'])) { - return new Collector\StatsD(); + return new StatsD(); } + if (isset($options['host']) && !isset($options['port'])) { - return new Collector\StatsD($options['host']); + return new StatsD($options['host']); } + if (!isset($options['host']) && isset($options['port'])) { throw new MetricsException('You should specified a host if you specified a port.'); } - $prefix = isset($options['prefix']) ? $options['prefix'] : ''; + $prefix = $options['prefix'] ?? ''; - return new Collector\StatsD($options['host'], $options['port'], $prefix); + return new StatsD($options['host'], $options['port'], $prefix); case 'dogstatsd': if ((!isset($options['host']) || !isset($options['port'])) && isset($options['prefix'])) { throw new MetricsException('You should specified a host and a port if you specified a prefix.'); } + if (!isset($options['host']) && !isset($options['port'])) { - return new Collector\DogStatsD(); + return new DogStatsD(); } + if (isset($options['host']) && !isset($options['port'])) { - return new Collector\DogStatsD($options['host']); + return new DogStatsD($options['host']); } + if (!isset($options['host']) && isset($options['port'])) { throw new MetricsException('You should specified a host if you specified a port.'); } - $prefix = isset($options['prefix']) ? $options['prefix'] : ''; + $prefix = $options['prefix'] ?? ''; - return new Collector\DogStatsD($options['host'], $options['port'], $prefix); + return new DogStatsD($options['host'], $options['port'], $prefix); case 'telegraf': if ((!isset($options['host']) || !isset($options['port'])) && isset($options['prefix'])) { throw new MetricsException('You should specified a host and a port if you specified a prefix.'); } + if (!isset($options['host']) && !isset($options['port'])) { - return new Collector\Telegraf(); + return new Telegraf(); } + if (isset($options['host']) && !isset($options['port'])) { - return new Collector\Telegraf($options['host']); + return new Telegraf($options['host']); } + if (!isset($options['host']) && isset($options['port'])) { throw new MetricsException('You should specified a host if you specified a port.'); } - $prefix = isset($options['prefix']) ? $options['prefix'] : ''; + $prefix = $options['prefix'] ?? ''; - return new Collector\Telegraf($options['host'], $options['port'], $prefix); + return new Telegraf($options['host'], $options['port'], $prefix); case 'graphite': if (!isset($options['host']) && !isset($options['port'])) { - return new Collector\Graphite(); + return new Graphite(); } + if (isset($options['host']) && !isset($options['port'])) { - return new Collector\Graphite($options['host']); + return new Graphite($options['host']); } + if (!isset($options['host']) && isset($options['port'])) { throw new MetricsException('You should specified a host if you specified a port.'); } - return new Collector\Graphite($options['host'], $options['port']); + return new Graphite($options['host'], $options['port']); case 'librato': if (!isset($options['hostname'])) { @@ -116,43 +136,43 @@ public static function create($type, array $options = array()) throw new MetricsException('No password given for librato collector.'); } - return new Collector\Librato(self::getHttpClient(), $options['hostname'], $options['username'], $options['password']); + return new Librato(self::getHttpClient(), $options['hostname'], $options['username'], $options['password']); case 'doctrine_dbal': if (!isset($options['connection'])) { throw new MetricsException('connection is required for Doctrine DBAL collector.'); } - return new Collector\DoctrineDBAL($options['connection']); + return new DoctrineDBAL($options['connection']); case 'logger': if (!isset($options['logger'])) { throw new MetricsException("Missing 'logger' key with logger service."); } - return new Collector\Logger($options['logger']); + return new Logger($options['logger']); case 'influxdb': if (!isset($options['client'])) { - throw new MetricsException('Missing \'client\' key for InfluxDB collector.'); + throw new MetricsException("Missing 'client' key for InfluxDB collector."); } - return new Collector\InfluxDB($options['client']); + return new InfluxDB($options['client']); case 'null': - return new Collector\NullCollector(); + return new NullCollector(); case 'null_inlinetaggable': - return new Collector\InlineTaggableGaugeableNullCollector(); + return new InlineTaggableGaugeableNullCollector(); case 'prometheus': if (!isset($options['collector_registry'])) { - throw new MetricsException('Missing \'collector_registry\' key for Prometheus collector.'); + throw new MetricsException("Missing 'collector_registry' key for Prometheus collector."); } - $namespace = isset($options['namespace']) ? $options['namespace'] : ''; + $namespace = $options['namespace'] ?? ''; - return new Collector\Prometheus($options['collector_registry'], $namespace); + return new Prometheus($options['collector_registry'], $namespace); default: throw new MetricsException(sprintf('Unknown metrics collector given (%s).', $type)); diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index f943c17..5d8c51f 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -13,10 +13,7 @@ class BeberleiMetricsExtension extends Extension { - /** - * @return void - */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -39,24 +36,16 @@ public function load(array $configs, ContainerBuilder $container) $container->setAlias(Collector::class, 'beberlei_metrics.collector'); } - private function createCollector($type, $config) + private function createCollector($type, array $config) { $definition = new ChildDefinition('beberlei_metrics.collector_proto.'.$config['type']); // Theses listeners should be as late as possible - $definition->addTag('kernel.event_listener', array( - 'method' => 'flush', - 'priority' => -1024, - 'event' => 'kernel.terminate', - )); - $definition->addTag('kernel.event_listener', array( - 'method' => 'flush', - 'priority' => -1024, - 'event' => 'console.terminate', - )); + $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'kernel.terminate']); + $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'console.terminate']); if (count($config['tags']) > 0) { - $definition->addMethodCall('setTags', array($config['tags'])); + $definition->addMethodCall('setTags', [$config['tags']]); } switch ($type) { diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index 3fee12e..4eb62cd 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -45,32 +45,24 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('prometheus_collector_registry')->defaultNull()->info('It must to contain service id for Prometheus\\CollectorRegistry class instance.')->end() ->scalarNode('namespace')->defaultValue('')->end() ->arrayNode('tags') - ->defaultValue(array()) + ->defaultValue([]) ->prototype('scalar')->end() ->end() ->end() ->validate() - ->ifTrue(function ($v) { - return 'librato' === $v['type'] && empty($v['source']); - }) + ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['source'])) ->thenInvalid('The source has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(function ($v) { - return 'librato' === $v['type'] && empty($v['username']); - }) + ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['username'])) ->thenInvalid('The username has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(function ($v) { - return 'librato' === $v['type'] && empty($v['password']); - }) + ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['password'])) ->thenInvalid('The password has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(function ($v) { - return 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry']); - }) + ->ifTrue(static fn($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') ->end() ->end() diff --git a/tests/Metrics/Collector/InMemoryTest.php b/tests/Metrics/Collector/InMemoryTest.php index 96116cc..259b8b0 100644 --- a/tests/Metrics/Collector/InMemoryTest.php +++ b/tests/Metrics/Collector/InMemoryTest.php @@ -18,18 +18,18 @@ class InMemoryTest extends TestCase { - const VARIABLE_A = 'variable_a'; - const VARIABLE_B = 'variable_b'; + public const VARIABLE_A = 'variable_a'; - /** @var InMemory */ - private $collector; + public const VARIABLE_B = 'variable_b'; + + private InMemory $collector; protected function setUp(): void { $this->collector = new InMemory(); } - public function testIncrement() + public function testIncrement(): void { $this->collector->increment(self::VARIABLE_A); $this->collector->increment(self::VARIABLE_A); @@ -40,7 +40,7 @@ public function testIncrement() $this->assertEquals(1, $this->collector->getMeasure(self::VARIABLE_B)); } - public function testDecrement() + public function testDecrement(): void { $this->collector->increment(self::VARIABLE_A); $this->collector->increment(self::VARIABLE_A); @@ -53,7 +53,7 @@ public function testDecrement() $this->assertEquals(-2, $this->collector->getMeasure(self::VARIABLE_B)); } - public function testTiming() + public function testTiming(): void { $this->collector->timing(self::VARIABLE_A, 123); @@ -64,7 +64,7 @@ public function testTiming() $this->assertEquals(112, $this->collector->getTiming(self::VARIABLE_B)); } - public function testMeasure() + public function testMeasure(): void { $this->collector->measure(self::VARIABLE_A, 2); $this->collector->measure(self::VARIABLE_A, -5); @@ -76,7 +76,7 @@ public function testMeasure() $this->assertEquals(123, $this->collector->getMeasure(self::VARIABLE_B)); } - public function testSettingGauge() + public function testSettingGauge(): void { $this->collector->gauge(self::VARIABLE_A, 2); $this->collector->gauge(self::VARIABLE_A, 5); @@ -88,7 +88,7 @@ public function testSettingGauge() $this->assertEquals(0, $this->collector->getGauge(self::VARIABLE_B)); } - public function testIncrementingGauge() + public function testIncrementingGauge(): void { $this->collector->gauge(self::VARIABLE_A, '10'); $this->collector->gauge(self::VARIABLE_A, '+2'); @@ -97,7 +97,7 @@ public function testIncrementingGauge() $this->assertEquals(9, $this->collector->getGauge(self::VARIABLE_A)); } - public function testSettingGaugeToNegativeValue() + public function testSettingGaugeToNegativeValue(): void { $this->collector->gauge(self::VARIABLE_A, 1); //sets to 1 $this->collector->gauge(self::VARIABLE_A, 2); //sets to 2 @@ -109,7 +109,7 @@ public function testSettingGaugeToNegativeValue() $this->assertEquals(-5, $this->collector->getGauge(self::VARIABLE_A)); } - public function testTypesOfMetricsAreSeparate() + public function testTypesOfMetricsAreSeparate(): void { $this->collector->increment(self::VARIABLE_A); $this->collector->gauge(self::VARIABLE_A, 2); @@ -120,7 +120,7 @@ public function testTypesOfMetricsAreSeparate() $this->assertEquals(3, $this->collector->getTiming(self::VARIABLE_A)); } - public function testFlushClearsData() + public function testFlushClearsData(): void { $this->collector->increment(self::VARIABLE_A); $this->collector->gauge(self::VARIABLE_A, 2); diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php index 7efc63d..f976324 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDBTest.php @@ -13,6 +13,7 @@ namespace Beberlei\Metrics\Tests\Collector; +use InfluxDB\Client; use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject; use Beberlei\Metrics\Collector\InfluxDB; @@ -24,30 +25,19 @@ class InfluxDBTest extends TestCase */ private $client; - /** - * @var InfluxDB - */ - private $collector; + private InfluxDB $collector; protected function setUp(): void { - $this->client = $this->getMockBuilder('\\InfluxDB\\Client') + $this->client = $this->getMockBuilder(Client::class) ->disableOriginalConstructor() ->getMock(); $this->collector = new InfluxDB($this->client); } - public function testCollectIncrement() + public function testCollectIncrement(): void { - $expectedArgs = array( - 'points' => array( - array( - 'measurement' => 'series-name', - 'fields' => array('value' => 1), - ), - ), - 'tags' => array(), - ); + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 1]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') @@ -57,17 +47,9 @@ public function testCollectIncrement() $this->collector->flush(); } - public function testCollectDecrement() + public function testCollectDecrement(): void { - $expectedArgs = array( - 'points' => array( - array( - 'measurement' => 'series-name', - 'fields' => array('value' => -1), - ), - ), - 'tags' => array(), - ); + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => -1]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') @@ -77,17 +59,9 @@ public function testCollectDecrement() $this->collector->flush(); } - public function testCollectTiming() + public function testCollectTiming(): void { - $expectedArgs = array( - 'points' => array( - array( - 'measurement' => 'series-name', - 'fields' => array('value' => 47.11), - ), - ), - 'tags' => array(), - ); + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') @@ -97,17 +71,9 @@ public function testCollectTiming() $this->collector->flush(); } - public function testCollectMeasure() + public function testCollectMeasure(): void { - $expectedArgs = array( - 'points' => array( - array( - 'measurement' => 'series-name', - 'fields' => array('value' => 47.11), - ), - ), - 'tags' => array(), - ); + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') @@ -117,22 +83,11 @@ public function testCollectMeasure() $this->collector->flush(); } - public function testCollectMeasureWithTags() + public function testCollectMeasureWithTags(): void { - $expectedTags = array( - 'dc' => 'west', - 'node' => 'nemesis101', - ); - - $expectedArgs = array( - 'points' => array( - array( - 'measurement' => 'series-name', - 'fields' => array('value' => 47.11), - ), - ), - 'tags' => $expectedTags, - ); + $expectedTags = ['dc' => 'west', 'node' => 'nemesis101']; + + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => $expectedTags]; $this->client->expects($this->once()) ->method('mark') diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index 94a25fa..895c258 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -13,28 +13,28 @@ namespace Beberlei\Metrics\Tests\Collector; +use Prometheus\CollectorRegistry; +use Prometheus\Gauge; use Beberlei\Metrics\Collector\Prometheus; use PHPUnit\Framework\TestCase; use Prometheus\Exception\MetricNotFoundException; class PrometheusTest extends TestCase { - const TEST_NAMESPACE = 'some_metric_namespace'; - const TEST_VARIABLE_NAME = 'some_variable_name'; + public const TEST_NAMESPACE = 'some_metric_namespace'; + + public const TEST_VARIABLE_NAME = 'some_variable_name'; /** * @var \PHPUnit_Framework_MockObject_MockObject */ private $collectorRegistryMock; - /** - * @var Prometheus - */ - private $collector; + private Prometheus $collector; protected function setUp(): void { - $this->collectorRegistryMock = $this->getMockBuilder('\\Prometheus\\CollectorRegistry') + $this->collectorRegistryMock = $this->getMockBuilder(CollectorRegistry::class) ->disableOriginalConstructor() ->getMock() ; @@ -42,18 +42,18 @@ protected function setUp(): void $this->collector = new Prometheus($this->collectorRegistryMock, self::TEST_NAMESPACE); } - public function testMeasure() + public function testMeasure(): void { $expectedVariableValue = 123; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; $gaugeMock ->expects($this->once()) ->method('set') - ->with($expectedVariableValue, array()) + ->with($expectedVariableValue, []) ; $this->collectorRegistryMock @@ -67,12 +67,12 @@ public function testMeasure() $this->collector->flush(); } - public function testMeasureWithTags() + public function testMeasureWithTags(): void { $expectedVariableValue = 123; - $expectedTagsValues = array('value1', 'value2'); + $expectedTagsValues = ['value1', 'value2']; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; @@ -89,25 +89,22 @@ public function testMeasureWithTags() ->willReturn($gaugeMock) ; - $this->collector->setTags(array( - 'tag1' => 'value1', - 'tag2' => 'value2', - )); + $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); } - public function testIncrement() + public function testIncrement(): void { - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; $gaugeMock ->expects($this->once()) ->method('inc') - ->with(array()) + ->with([]) ; $this->collectorRegistryMock @@ -121,11 +118,11 @@ public function testIncrement() $this->collector->flush(); } - public function testIncrementWithTags() + public function testIncrementWithTags(): void { - $expectedTagsValues = array('value1', 'value2'); + $expectedTagsValues = ['value1', 'value2']; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; @@ -142,25 +139,22 @@ public function testIncrementWithTags() ->willReturn($gaugeMock) ; - $this->collector->setTags(array( - 'tag1' => 'value1', - 'tag2' => 'value2', - )); + $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); $this->collector->increment(self::TEST_VARIABLE_NAME); $this->collector->flush(); } - public function testDecrement() + public function testDecrement(): void { - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; $gaugeMock ->expects($this->once()) ->method('dec') - ->with(array()) + ->with([]) ; $this->collectorRegistryMock @@ -174,11 +168,11 @@ public function testDecrement() $this->collector->flush(); } - public function testDecrementWithTags() + public function testDecrementWithTags(): void { - $expectedTagsValues = array('value1', 'value2'); + $expectedTagsValues = ['value1', 'value2']; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; @@ -195,27 +189,24 @@ public function testDecrementWithTags() ->willReturn($gaugeMock) ; - $this->collector->setTags(array( - 'tag1' => 'value1', - 'tag2' => 'value2', - )); + $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); $this->collector->decrement(self::TEST_VARIABLE_NAME); $this->collector->flush(); } - public function testTiming() + public function testTiming(): void { $expectedVariableValue = 123; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; $gaugeMock ->expects($this->once()) ->method('set') - ->with($expectedVariableValue, array()) + ->with($expectedVariableValue, []) ; $this->collectorRegistryMock @@ -229,12 +220,12 @@ public function testTiming() $this->collector->flush(); } - public function testTimingWithTags() + public function testTimingWithTags(): void { $expectedVariableValue = 123; - $expectedTagsValues = array('value1', 'value2'); + $expectedTagsValues = ['value1', 'value2']; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; @@ -251,22 +242,19 @@ public function testTimingWithTags() ->willReturn($gaugeMock) ; - $this->collector->setTags(array( - 'tag1' => 'value1', - 'tag2' => 'value2', - )); + $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); $this->collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); } - public function testMeasureWhenSetNewVariableWithTags() + public function testMeasureWhenSetNewVariableWithTags(): void { $expectedVariableValue = 123; - $expectedTagsNames = array('tag1', 'tag2'); - $expectedTagsValues = array('value1', 'value2'); + $expectedTagsNames = ['tag1', 'tag2']; + $expectedTagsValues = ['value1', 'value2']; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; @@ -289,10 +277,7 @@ public function testMeasureWhenSetNewVariableWithTags() ->willReturn($gaugeMock) ; - $this->collector->setTags(array( - 'tag1' => 'value1', - 'tag2' => 'value2', - )); + $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); @@ -301,24 +286,24 @@ public function testMeasureWhenSetNewVariableWithTags() /** * Method flush must to reset value of field `data`. */ - public function testFlushWhenCallsTwiceWithDifferentData() + public function testFlushWhenCallsTwiceWithDifferentData(): void { $firstExpectedVariableValue = 123; $secondExpectedVariableValue = 321; - $gaugeMock = $this->getMockBuilder('\\Prometheus\\Gauge') + $gaugeMock = $this->getMockBuilder(Gauge::class) ->disableOriginalConstructor() ->getMock() ; $gaugeMock ->expects($this->at(0)) ->method('set') - ->with($firstExpectedVariableValue, array()) + ->with($firstExpectedVariableValue, []) ; $gaugeMock ->expects($this->at(1)) ->method('set') - ->with($secondExpectedVariableValue, array()) + ->with($secondExpectedVariableValue, []) ; $this->collectorRegistryMock diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index 4fa931c..a1581a4 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -2,78 +2,56 @@ namespace Beberlei\Metrics\Tests; +use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\DogStatsD; +use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\Librato; +use Beberlei\Metrics\Collector\DoctrineDBAL; +use Doctrine\DBAL\Connection; +use Beberlei\Metrics\Collector\Logger; +use Beberlei\Metrics\Collector\NullCollector; +use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; +use Beberlei\Metrics\Collector\InfluxDB; +use InfluxDB\Client; +use Beberlei\Metrics\Collector\Prometheus; +use Prometheus\CollectorRegistry; +use Beberlei\Metrics\MetricsException; use Beberlei\Metrics\Factory; use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; class FactoryTest extends TestCase { - public function getCreateValidMetricTests() + public function getCreateValidMetricTests(): array { - return array( - array('Beberlei\Metrics\Collector\StatsD', 'statsd'), - array('Beberlei\Metrics\Collector\StatsD', 'statsd', array('host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix')), - array('Beberlei\Metrics\Collector\StatsD', 'statsd', array('host' => 'localhost', 'port' => 1234)), - array('Beberlei\Metrics\Collector\StatsD', 'statsd', array('host' => 'localhost')), - array('Beberlei\Metrics\Collector\DogStatsD', 'dogstatsd'), - array('Beberlei\Metrics\Collector\DogStatsD', 'dogstatsd', array('host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix')), - array('Beberlei\Metrics\Collector\DogStatsD', 'dogstatsd', array('host' => 'localhost', 'port' => 1234)), - array('Beberlei\Metrics\Collector\DogStatsD', 'dogstatsd', array('host' => 'localhost')), - array('Beberlei\Metrics\Collector\Graphite', 'graphite'), - array('Beberlei\Metrics\Collector\Graphite', 'graphite', array('host' => 'localhost', 'port' => 1234)), - array('Beberlei\Metrics\Collector\Librato', 'librato', array('hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password')), - array('Beberlei\Metrics\Collector\DoctrineDBAL', 'doctrine_dbal', array('connection' => $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock())), - array('Beberlei\Metrics\Collector\Logger', 'logger', array('logger' => new NullLogger())), - array('Beberlei\Metrics\Collector\NullCollector', 'null'), - array('Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector', 'null_inlinetaggable'), - array('Beberlei\Metrics\Collector\InfluxDB', 'influxdb', array('client' => $this->getMockBuilder('\\InfluxDB\\Client')->disableOriginalConstructor()->getMock())), - array('Beberlei\Metrics\Collector\Prometheus', 'prometheus', array('collector_registry' => $this->getMockBuilder('\\Prometheus\\CollectorRegistry')->disableOriginalConstructor()->getMock())), - array('Beberlei\Metrics\Collector\Prometheus', 'prometheus', array('collector_registry' => $this->getMockBuilder('\\Prometheus\\CollectorRegistry')->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace')), - ); + return [[StatsD::class, 'statsd'], [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']], [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234]], [StatsD::class, 'statsd', ['host' => 'localhost']], [DogStatsD::class, 'dogstatsd'], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234]], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost']], [Graphite::class, 'graphite'], [Graphite::class, 'graphite', ['host' => 'localhost', 'port' => 1234]], [Librato::class, 'librato', ['hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password']], [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]], [Logger::class, 'logger', ['logger' => new NullLogger()]], [NullCollector::class, 'null'], [InlineTaggableGaugeableNullCollector::class, 'null_inlinetaggable'], [InfluxDB::class, 'influxdb', ['client' => $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock()]], [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock()]], [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace']]]; } /** * @dataProvider getCreateValidMetricTests */ - public function testCreateValidMetric($expectedClass, $type, $options = array()) + public function testCreateValidMetric(string $expectedClass, string $type, array $options = []): void { $this->assertInstanceOf($expectedClass, Factory::create($type, $options)); } - public function getCreateThrowExceptionIfOptionsAreInvalidTests() + public function getCreateThrowExceptionIfOptionsAreInvalidTests(): array { - return array( - array('You should specified a host if you specified a port.', 'statsd', array('port' => '1234')), - array('You should specified a host and a port if you specified a prefix.', 'statsd', array('prefix' => 'prefix')), - array('You should specified a host and a port if you specified a prefix.', 'statsd', array('port' => '1234', 'prefix' => 'prefix')), - array('You should specified a host and a port if you specified a prefix.', 'statsd', array('hostname' => 'foobar.com', 'prefix' => 'prefix')), - array('You should specified a host if you specified a port.', 'dogstatsd', array('port' => '1234')), - array('You should specified a host and a port if you specified a prefix.', 'dogstatsd', array('prefix' => 'prefix')), - array('You should specified a host and a port if you specified a prefix.', 'dogstatsd', array('port' => '1234', 'prefix' => 'prefix')), - array('You should specified a host and a port if you specified a prefix.', 'dogstatsd', array('hostname' => 'foobar.com', 'prefix' => 'prefix')), - array('You should specified a host if you specified a port.', 'graphite', array('port' => '1234')), - array('Hostname is required for librato collector.', 'librato'), - array('No username given for librato collector.', 'librato', array('hostname' => 'foobar.com')), - array('No password given for librato collector.', 'librato', array('hostname' => 'foobar.com', 'username' => 'username')), - array('connection is required for Doctrine DBAL collector.', 'doctrine_dbal'), - array('Missing \'logger\' key with logger service.', 'logger'), - array('Missing \'client\' key for InfluxDB collector.', 'influxdb'), - array('Missing \'collector_registry\' key for Prometheus collector.', 'prometheus'), - ); + return [['You should specified a host if you specified a port.', 'statsd', ['port' => '1234']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['port' => '1234', 'prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']], ['You should specified a host if you specified a port.', 'dogstatsd', ['port' => '1234']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['port' => '1234', 'prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']], ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']], ['Hostname is required for librato collector.', 'librato'], ['No username given for librato collector.', 'librato', ['hostname' => 'foobar.com']], ['No password given for librato collector.', 'librato', ['hostname' => 'foobar.com', 'username' => 'username']], ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal'], ["Missing 'logger' key with logger service.", 'logger'], ["Missing 'client' key for InfluxDB collector.", 'influxdb'], ["Missing 'collector_registry' key for Prometheus collector.", 'prometheus']]; } /** * @dataProvider getCreateThrowExceptionIfOptionsAreInvalidTests */ - public function testCreateThrowExceptionIfOptionsAreInvalid($expectedMessage, $type, $options = array()) + public function testCreateThrowExceptionIfOptionsAreInvalid(string $expectedMessage, string $type, array $options = []): void { try { Factory::create($type, $options); $this->fail('An expected exception (MetricsException) has not been raised.'); - } catch (\Exception $e) { - $this->assertInstanceOf('Beberlei\Metrics\MetricsException', $e); - $this->assertSame($expectedMessage, $e->getMessage()); + } catch (\Exception $exception) { + $this->assertInstanceOf(MetricsException::class, $exception); + $this->assertSame($expectedMessage, $exception->getMessage()); } } } diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index a301784..c739b54 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -13,6 +13,19 @@ namespace Beberlei\Bundle\MetricsBundle\Tests\DependencyInjection; +use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\Librato; +use Beberlei\Metrics\Collector\Logger; +use Beberlei\Metrics\Collector\NullCollector; +use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\DogStatsD; +use Beberlei\Metrics\Collector\Telegraf; +use InfluxDB\Client; +use Beberlei\Metrics\Collector\InfluxDB; +use Prometheus\CollectorRegistry; +use Beberlei\Metrics\Collector\Prometheus; +use Beberlei\Metrics\Collector\InMemory; +use Psr\Log\NullLogger; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -20,34 +33,18 @@ class BeberleiMetricsExtensionTest extends TestCase { - public function testWithGraphite() + public function testWithGraphite(): void { - $container = $this->createContainer(array( - 'default' => 'simple', - 'collectors' => array( - 'simple' => array( - 'type' => 'graphite', - ), - 'full' => array( - 'type' => 'graphite', - 'host' => 'graphite.localhost', - 'port' => 1234, - 'protocol' => 'udp', - ), - ), - ), array( - 'beberlei_metrics.collector.simple', - 'beberlei_metrics.collector.full' - )); + $container = $this->createContainer(['default' => 'simple', 'collectors' => ['simple' => ['type' => 'graphite'], 'full' => ['type' => 'graphite', 'host' => 'graphite.localhost', 'port' => 1234, 'protocol' => 'udp']]], ['beberlei_metrics.collector.simple', 'beberlei_metrics.collector.full']); $collector = $container->get('beberlei_metrics.collector.simple'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Graphite', $collector); + $this->assertInstanceOf(Graphite::class, $collector); $this->assertSame('tcp', $this->getProperty($collector, 'protocol')); $this->assertSame('localhost', $this->getProperty($collector, 'host')); $this->assertSame(2003, $this->getProperty($collector, 'port')); $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Graphite', $collector); + $this->assertInstanceOf(Graphite::class, $collector); $this->assertSame('udp', $this->getProperty($collector, 'protocol')); $this->assertSame('graphite.localhost', $this->getProperty($collector, 'host')); $this->assertSame(1234, $this->getProperty($collector, 'port')); @@ -57,165 +54,86 @@ public function testWithGraphite() * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException * The source has to be specified to use a Librato */ - public function testWithLibratoAndInvalidConfiguration() + public function testWithLibratoAndInvalidConfiguration(): void { - $container = $this->createContainer(array( - 'collectors' => array( - 'simple' => array( - 'type' => 'librato', - ), - ), - ), array('beberlei_metrics.collector.librato')); - - $this->assertInstanceOf('Beberlei\Metrics\Collector\Librato', $container->get('beberlei_metrics.collector.librato')); + $container = $this->createContainer(['collectors' => ['simple' => ['type' => 'librato']]], ['beberlei_metrics.collector.librato']); + + $this->assertInstanceOf(Librato::class, $container->get('beberlei_metrics.collector.librato')); } - public function testWithLibrato() + public function testWithLibrato(): void { - $container = $this->createContainer(array( - 'collectors' => array( - 'full' => array( - 'type' => 'librato', - 'source' => 'foo.beberlei.de', - 'username' => 'foo', - 'password' => 'bar', - ), - ), - ), array('beberlei_metrics.collector.full')); + $container = $this->createContainer(['collectors' => ['full' => ['type' => 'librato', 'source' => 'foo.beberlei.de', 'username' => 'foo', 'password' => 'bar']]], ['beberlei_metrics.collector.full']); $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Librato', $collector); + $this->assertInstanceOf(Librato::class, $collector); $this->assertSame('foo.beberlei.de', $this->getProperty($collector, 'source')); $this->assertSame('foo', $this->getProperty($collector, 'username')); $this->assertSame('bar', $this->getProperty($collector, 'password')); } - public function testWithLogger() + public function testWithLogger(): void { - $container = $this->createContainer(array( - 'collectors' => array( - 'logger' => array( - 'type' => 'logger', - ), - ), - ), array('beberlei_metrics.collector.logger')); - - $this->assertInstanceOf('Beberlei\Metrics\Collector\Logger', $container->get('beberlei_metrics.collector.logger')); + $container = $this->createContainer(['collectors' => ['logger' => ['type' => 'logger']]], ['beberlei_metrics.collector.logger']); + + $this->assertInstanceOf(Logger::class, $container->get('beberlei_metrics.collector.logger')); } - public function testWithNullCollector() + public function testWithNullCollector(): void { - $container = $this->createContainer(array( - 'collectors' => array( - 'null' => array( - 'type' => 'null', - ), - ), - ), array('beberlei_metrics.collector.null')); - - $this->assertInstanceOf('Beberlei\Metrics\Collector\NullCollector', $container->get('beberlei_metrics.collector.null')); + $container = $this->createContainer(['collectors' => ['null' => ['type' => 'null']]], ['beberlei_metrics.collector.null']); + + $this->assertInstanceOf(NullCollector::class, $container->get('beberlei_metrics.collector.null')); } - public function testWithStatsD() + public function testWithStatsD(): void { - $container = $this->createContainer(array( - 'default' => 'simple', - 'collectors' => array( - 'simple' => array( - 'type' => 'statsd', - ), - 'full' => array( - 'type' => 'statsd', - 'host' => 'statsd.localhost', - 'port' => 1234, - 'prefix' => 'application.com.symfony.', - ), - ), - ), array( - 'beberlei_metrics.collector.simple', - 'beberlei_metrics.collector.full' - )); + $container = $this->createContainer(['default' => 'simple', 'collectors' => ['simple' => ['type' => 'statsd'], 'full' => ['type' => 'statsd', 'host' => 'statsd.localhost', 'port' => 1234, 'prefix' => 'application.com.symfony.']]], ['beberlei_metrics.collector.simple', 'beberlei_metrics.collector.full']); $collector = $container->get('beberlei_metrics.collector.simple'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\StatsD', $collector); + $this->assertInstanceOf(StatsD::class, $collector); $this->assertSame('localhost', $this->getProperty($collector, 'host')); $this->assertSame(8125, $this->getProperty($collector, 'port')); $this->assertSame('', $this->getProperty($collector, 'prefix')); $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\StatsD', $collector); + $this->assertInstanceOf(StatsD::class, $collector); $this->assertSame('statsd.localhost', $this->getProperty($collector, 'host')); $this->assertSame(1234, $this->getProperty($collector, 'port')); $this->assertSame('application.com.symfony.', $this->getProperty($collector, 'prefix')); } - public function testWithDogStatsD() + public function testWithDogStatsD(): void { - $container = $this->createContainer(array( - 'default' => 'simple', - 'collectors' => array( - 'simple' => array( - 'type' => 'dogstatsd', - ), - 'full' => array( - 'type' => 'dogstatsd', - 'host' => 'dogstatsd.localhost', - 'port' => 1234, - 'prefix' => 'application.com.symfony.', - ), - ), - ), array( - 'beberlei_metrics.collector.simple', - 'beberlei_metrics.collector.full' - )); + $container = $this->createContainer(['default' => 'simple', 'collectors' => ['simple' => ['type' => 'dogstatsd'], 'full' => ['type' => 'dogstatsd', 'host' => 'dogstatsd.localhost', 'port' => 1234, 'prefix' => 'application.com.symfony.']]], ['beberlei_metrics.collector.simple', 'beberlei_metrics.collector.full']); $collector = $container->get('beberlei_metrics.collector.simple'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\DogStatsD', $collector); + $this->assertInstanceOf(DogStatsD::class, $collector); $this->assertSame('localhost', $this->getProperty($collector, 'host')); $this->assertSame(8125, $this->getProperty($collector, 'port')); $this->assertSame('', $this->getProperty($collector, 'prefix')); $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\DogStatsD', $collector); + $this->assertInstanceOf(DogStatsD::class, $collector); $this->assertSame('dogstatsd.localhost', $this->getProperty($collector, 'host')); $this->assertSame(1234, $this->getProperty($collector, 'port')); $this->assertSame('application.com.symfony.', $this->getProperty($collector, 'prefix')); } - public function testWithTelegraf() + public function testWithTelegraf(): void { - $expectedTags = array( - 'string_tag' => 'first_value', - 'int_tag' => 123, - ); - - $container = $this->createContainer(array( - 'default' => 'simple', - 'collectors' => array( - 'simple' => array( - 'type' => 'telegraf', - ), - 'full' => array( - 'type' => 'telegraf', - 'host' => 'telegraf.localhost', - 'port' => 1234, - 'prefix' => 'application.com.symfony.', - 'tags' => $expectedTags, - ), - ), - ), array( - 'beberlei_metrics.collector.simple', - 'beberlei_metrics.collector.full' - )); + $expectedTags = ['string_tag' => 'first_value', 'int_tag' => 123]; + + $container = $this->createContainer(['default' => 'simple', 'collectors' => ['simple' => ['type' => 'telegraf'], 'full' => ['type' => 'telegraf', 'host' => 'telegraf.localhost', 'port' => 1234, 'prefix' => 'application.com.symfony.', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.simple', 'beberlei_metrics.collector.full']); $collector = $container->get('beberlei_metrics.collector.simple'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Telegraf', $collector); + $this->assertInstanceOf(Telegraf::class, $collector); $this->assertSame('localhost', $this->getProperty($collector, 'host')); $this->assertSame(8125, $this->getProperty($collector, 'port')); $this->assertSame('', $this->getProperty($collector, 'prefix')); $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Telegraf', $collector); + $this->assertInstanceOf(Telegraf::class, $collector); $this->assertSame('telegraf.localhost', $this->getProperty($collector, 'host')); $this->assertSame(1234, $this->getProperty($collector, 'port')); $this->assertSame('application.com.symfony.', $this->getProperty($collector, 'prefix')); @@ -223,148 +141,88 @@ public function testWithTelegraf() $this->assertEquals(',string_tag=first_value,int_tag=123', $this->getProperty($collector, 'tags')); } - public function testWithInfluxDB() + public function testWithInfluxDB(): void { - $influxDBClientMock = $this->getMockBuilder('InfluxDB\Client') + $influxDBClientMock = $this->getMockBuilder(Client::class) ->disableOriginalConstructor() ->getMock() ; - $container = $this->createContainer(array( - 'collectors' => array( - 'influxdb' => array( - 'type' => 'influxdb', - 'influxdb_client' => 'influxdb_client_mock', - ), - ), - ), array('beberlei_metrics.collector.influxdb'), array( - 'influxdb_client_mock' => $influxDBClientMock, - )); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'influxdb_client' => 'influxdb_client_mock']]], ['beberlei_metrics.collector.influxdb'], ['influxdb_client_mock' => $influxDBClientMock]); $collector = $container->get('beberlei_metrics.collector.influxdb'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\InfluxDB', $collector); + $this->assertInstanceOf(InfluxDB::class, $collector); $this->assertSame($influxDBClientMock, $this->getProperty($collector, 'client')); } - public function testWithInfluxDBAndWithTags() + public function testWithInfluxDBAndWithTags(): void { - $expectedTags = array( - 'string_tag' => 'first_value', - 'int_tag' => 123, - ); + $expectedTags = ['string_tag' => 'first_value', 'int_tag' => 123]; - $influxDBClientMock = $this->getMockBuilder('InfluxDB\Client') + $influxDBClientMock = $this->getMockBuilder(Client::class) ->disableOriginalConstructor() ->getMock() ; - $container = $this->createContainer(array( - 'collectors' => array( - 'influxdb' => array( - 'type' => 'influxdb', - 'influxdb_client' => 'influxdb_client_mock', - 'tags' => $expectedTags, - ), - ), - ), array('beberlei_metrics.collector.influxdb'), array( - 'influxdb_client_mock' => $influxDBClientMock, - )); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'influxdb_client' => 'influxdb_client_mock', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.influxdb'], ['influxdb_client_mock' => $influxDBClientMock]); $collector = $container->get('beberlei_metrics.collector.influxdb'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\InfluxDB', $collector); + $this->assertInstanceOf(InfluxDB::class, $collector); $this->assertEquals($expectedTags, $this->getProperty($collector, 'tags')); } - public function testWithPrometheus() + public function testWithPrometheus(): void { - $prometheusCollectorRegistryMock = $this->getMockBuilder('\\Prometheus\\CollectorRegistry') + $prometheusCollectorRegistryMock = $this->getMockBuilder(CollectorRegistry::class) ->disableOriginalConstructor() ->getMock() ; - $container = $this->createContainer(array( - 'collectors' => array( - 'prometheus' => array( - 'type' => 'prometheus', - 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', - ), - ), - ), array('beberlei_metrics.collector.prometheus'), array( - 'prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock, - )); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock']]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Prometheus', $collector); + $this->assertInstanceOf(Prometheus::class, $collector); $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'collectorRegistry')); $this->assertSame('', $this->getProperty($collector, 'namespace')); } - public function testWithInMemory() + public function testWithInMemory(): void { - $container = $this->createContainer(array( - 'collectors' => array( - 'memory' => array( - 'type' => 'memory', - ), - ), - ), array('beberlei_metrics.collector.memory')); + $container = $this->createContainer(['collectors' => ['memory' => ['type' => 'memory']]], ['beberlei_metrics.collector.memory']); $collector = $container->get('beberlei_metrics.collector.memory'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\InMemory', $collector); + $this->assertInstanceOf(InMemory::class, $collector); } - public function testWithPrometheusAndWithNamespace() + public function testWithPrometheusAndWithNamespace(): void { $expectedNamespace = 'some_namespace'; - $prometheusCollectorRegistryMock = $this->getMockBuilder('\\Prometheus\\CollectorRegistry') + $prometheusCollectorRegistryMock = $this->getMockBuilder(CollectorRegistry::class) ->disableOriginalConstructor() ->getMock() ; - $container = $this->createContainer(array( - 'collectors' => array( - 'prometheus' => array( - 'type' => 'prometheus', - 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', - 'namespace' => $expectedNamespace, - ), - ), - ), array('beberlei_metrics.collector.prometheus'), array( - 'prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock, - )); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', 'namespace' => $expectedNamespace]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Prometheus', $collector); + $this->assertInstanceOf(Prometheus::class, $collector); $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'collectorRegistry')); $this->assertSame($expectedNamespace, $this->getProperty($collector, 'namespace')); } - public function testWithPrometheusAndWithTags() + public function testWithPrometheusAndWithTags(): void { - $expectedTags = array( - 'string_tag' => 'first_value', - 'int_tag' => 123, - ); + $expectedTags = ['string_tag' => 'first_value', 'int_tag' => 123]; - $prometheusCollectorRegistryMock = $this->getMockBuilder('\\Prometheus\\CollectorRegistry') + $prometheusCollectorRegistryMock = $this->getMockBuilder(CollectorRegistry::class) ->disableOriginalConstructor() ->getMock() ; - $container = $this->createContainer(array( - 'collectors' => array( - 'prometheus' => array( - 'type' => 'prometheus', - 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', - 'tags' => $expectedTags, - ), - ), - ), array('beberlei_metrics.collector.prometheus'), array( - 'prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock, - )); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); - $this->assertInstanceOf('Beberlei\Metrics\Collector\Prometheus', $collector); + $this->assertInstanceOf(Prometheus::class, $collector); $this->assertEquals($expectedTags, $this->getProperty($collector, 'tags')); } @@ -372,25 +230,19 @@ public function testWithPrometheusAndWithTags() * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException * @expectedExceptionMessage The prometheus_collector_registry has to be specified to use a Prometheus */ - public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified() + public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified(): void { - $this->createContainer(array( - 'collectors' => array( - 'prometheus' => array( - 'type' => 'prometheus', - ), - ), - )); + $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]]); } - private function createContainer($configs, $publicServices = array(), $additionalServices = array()) + private function createContainer($configs, array $publicServices = [], array $additionalServices = []): ContainerBuilder { $container = new ContainerBuilder(); $extension = new BeberleiMetricsExtension(); - $extension->load(array($configs), $container); + $extension->load([$configs], $container); // Needed for logger collector - $container->setDefinition('logger', new Definition('Psr\Log\NullLogger')); + $container->setDefinition('logger', new Definition(NullLogger::class)); foreach ($additionalServices as $serviceId => $additionalService) { $container->set($serviceId, $additionalService); @@ -405,9 +257,9 @@ private function createContainer($configs, $publicServices = array(), $additiona return $container; } - private function getProperty($object, $property) + private function getProperty(?object $object, string $property) { - $reflectionProperty = new \ReflectionProperty(get_class($object), $property); + $reflectionProperty = new \ReflectionProperty($object::class, $property); $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($object); From 871a1faf839baa1dddceb2bb7dbdba4033e4834b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 19:42:33 +0100 Subject: [PATCH 12/32] chore: add php-cs-fixer --- .php-cs-fixer.php | 30 + example/statsd.php | 4 +- src/Metrics/Collector/CollectorInterface.php | 2 +- src/Metrics/Collector/DoctrineDBAL.php | 3 +- src/Metrics/Collector/DogStatsD.php | 10 +- src/Metrics/Collector/Graphite.php | 9 +- src/Metrics/Collector/InMemory.php | 1 - src/Metrics/Collector/Logger.php | 4 +- src/Metrics/Collector/Prometheus.php | 2 +- src/Metrics/Collector/StatsD.php | 8 +- src/Metrics/Collector/Telegraf.php | 10 +- src/Metrics/Factory.php | 13 +- .../BeberleiMetricsExtension.php | 15 +- .../DependencyInjection/Configuration.php | 8 +- tests/Metrics/Collector/InMemoryTest.php | 6 +- tests/Metrics/Collector/InfluxDBTest.php | 23 +- tests/Metrics/Collector/PrometheusTest.php | 4 +- tests/Metrics/FactoryTest.php | 16 +- .../BeberleiMetricsExtensionTest.php | 15 +- tools/php-cs-fixer/.gitignore | 1 + tools/php-cs-fixer/composer.json | 5 + tools/php-cs-fixer/composer.lock | 1823 +++++++++++++++++ 22 files changed, 1936 insertions(+), 76 deletions(-) create mode 100644 .php-cs-fixer.php create mode 100644 tools/php-cs-fixer/.gitignore create mode 100644 tools/php-cs-fixer/composer.json create mode 100644 tools/php-cs-fixer/composer.lock diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..312ddf8 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,30 @@ +ignoreVCSIgnored(true) + ->in(__DIR__) + ->append([ + __FILE__, + ]) +; + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PHP81Migration' => true, + '@PhpCsFixer' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + 'heredoc_indentation' => false, + 'single_line_empty_body' => false, + 'ordered_types' => false, // From @PhpCsFixer but we don't want it + 'php_unit_internal_class' => false, // From @PhpCsFixer but we don't want it + 'php_unit_test_class_requires_covers' => false, // From @PhpCsFixer but we don't want it + 'phpdoc_add_missing_param_annotation' => false, // From @PhpCsFixer but we don't want it + 'concat_space' => ['spacing' => 'one'], + 'ordered_class_elements' => true, // Symfony(PSR12) override the default value, but we don't want + 'blank_line_before_statement' => true, // Symfony(PSR12) override the default value, but we don't want + 'method_chaining_indentation' => false, // Do not work well with Tree builder + ]) + ->setFinder($finder) +; diff --git a/example/statsd.php b/example/statsd.php index 2fb3b68..8ce6cc5 100644 --- a/example/statsd.php +++ b/example/statsd.php @@ -1,8 +1,8 @@ increment('foo.bar'); diff --git a/src/Metrics/Collector/CollectorInterface.php b/src/Metrics/Collector/CollectorInterface.php index 5431a87..0844c26 100644 --- a/src/Metrics/Collector/CollectorInterface.php +++ b/src/Metrics/Collector/CollectorInterface.php @@ -33,7 +33,7 @@ public function decrement(string $variable, array $tags = []): void; /** * Records a timing. * - * @param int $time The duration of the timing in milliseconds + * @param int $time The duration of the timing in milliseconds */ public function timing(string $variable, int $time, array $tags = []): void; diff --git a/src/Metrics/Collector/DoctrineDBAL.php b/src/Metrics/Collector/DoctrineDBAL.php index caf1b47..875072b 100644 --- a/src/Metrics/Collector/DoctrineDBAL.php +++ b/src/Metrics/Collector/DoctrineDBAL.php @@ -14,7 +14,6 @@ namespace Beberlei\Metrics\Collector; use Doctrine\DBAL\Connection; -use Exception; /** * Sends statistics to a relational database. @@ -77,7 +76,7 @@ public function flush(): void } $this->conn->commit(); - } catch (Exception) { + } catch (\Exception) { $this->conn->rollback(); } diff --git a/src/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php index 9fdbbdd..bfc167d 100644 --- a/src/Metrics/Collector/DogStatsD.php +++ b/src/Metrics/Collector/DogStatsD.php @@ -31,12 +31,12 @@ public function measure(string $variable, int $value, array $tags = []): void public function increment(string $variable, array $tags = []): void { - $this->data[] = $variable.':1|c'.$this->buildTagString($tags); + $this->data[] = $variable . ':1|c' . $this->buildTagString($tags); } public function decrement(string $variable, array $tags = []): void { - $this->data[] = $variable.':-1|c'.$this->buildTagString($tags); + $this->data[] = $variable . ':-1|c' . $this->buildTagString($tags); } public function timing(string $variable, int $time, array $tags = []): void @@ -55,7 +55,7 @@ public function flush(): void return; } - $fp = fsockopen('udp://'.$this->host, $this->port, $errno, $errstr, 1.0); + $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { return; @@ -63,7 +63,7 @@ public function flush(): void $level = error_reporting(0); foreach ($this->data as $line) { - fwrite($fp, $this->prefix.$line); + fwrite($fp, $this->prefix . $line); } error_reporting($level); @@ -87,7 +87,7 @@ private function buildTagString(array $tags): string $tagString = implode(',', $results); - if (strlen($tagString)) { + if (\strlen($tagString)) { $tagString = sprintf('|#%s', $tagString); } diff --git a/src/Metrics/Collector/Graphite.php b/src/Metrics/Collector/Graphite.php index bdd9b4a..1bc7711 100644 --- a/src/Metrics/Collector/Graphite.php +++ b/src/Metrics/Collector/Graphite.php @@ -13,8 +13,6 @@ namespace Beberlei\Metrics\Collector; -use Exception; - /** * Sends statistics to the stats daemon over UDP or TCP. */ @@ -34,7 +32,6 @@ public function measure(string $variable, int $value, array $tags = []): void $this->push($variable, $value); } - public function increment(string $variable, array $tags = []): void { $this->push($variable, 1); @@ -57,7 +54,7 @@ public function flush(): void } try { - $fp = fsockopen($this->protocol.'://'.$this->host, $this->port); + $fp = fsockopen($this->protocol . '://' . $this->host, $this->port); if (!$fp) { return; @@ -68,7 +65,7 @@ public function flush(): void } fclose($fp); - } catch (Exception) { + } catch (\Exception) { } $this->data = []; @@ -77,7 +74,7 @@ public function flush(): void public function push(string $variable, int|float $value, ?int $time = null): void { $this->data[] = sprintf( - is_float($value) ? "%s %.18f %d\n" : "%s %d %d\n", + \is_float($value) ? "%s %.18f %d\n" : "%s %d %d\n", $variable, $value, $time ?: time() diff --git a/src/Metrics/Collector/InMemory.php b/src/Metrics/Collector/InMemory.php index bae39f3..3b63564 100644 --- a/src/Metrics/Collector/InMemory.php +++ b/src/Metrics/Collector/InMemory.php @@ -65,7 +65,6 @@ public function flush(): void $this->incrementData = []; } - public function getMeasure(string $variable): int { return $this->incrementData[$variable] ?? 0; diff --git a/src/Metrics/Collector/Logger.php b/src/Metrics/Collector/Logger.php index 0a294d8..04593be 100644 --- a/src/Metrics/Collector/Logger.php +++ b/src/Metrics/Collector/Logger.php @@ -29,12 +29,12 @@ public function measure(string $variable, int $value, array $tags = []): void public function increment(string $variable, array $tags = []): void { - $this->logger->debug('increment:'.$variable); + $this->logger->debug('increment:' . $variable); } public function decrement(string $variable, array $tags = []): void { - $this->logger->debug('decrement:'.$variable); + $this->logger->debug('decrement:' . $variable); } public function timing(string $variable, int $time, array $tags = []): void diff --git a/src/Metrics/Collector/Prometheus.php b/src/Metrics/Collector/Prometheus.php index 6945adf..4c18439 100644 --- a/src/Metrics/Collector/Prometheus.php +++ b/src/Metrics/Collector/Prometheus.php @@ -13,9 +13,9 @@ namespace Beberlei\Metrics\Collector; -use Prometheus\Gauge; use Prometheus\CollectorRegistry; use Prometheus\Exception\MetricNotFoundException; +use Prometheus\Gauge; class Prometheus implements CollectorInterface, TaggableCollectorInterface { diff --git a/src/Metrics/Collector/StatsD.php b/src/Metrics/Collector/StatsD.php index 4ae2fc8..5fc901f 100644 --- a/src/Metrics/Collector/StatsD.php +++ b/src/Metrics/Collector/StatsD.php @@ -34,12 +34,12 @@ public function timing(string $variable, int $time, array $tags = []): void public function increment(string $variable, array $tags = []): void { - $this->data[] = $variable.':1|c'; + $this->data[] = $variable . ':1|c'; } public function decrement(string $variable, array $tags = []): void { - $this->data[] = $variable.':-1|c'; + $this->data[] = $variable . ':-1|c'; } public function measure(string $variable, int $value, array $tags = []): void @@ -58,7 +58,7 @@ public function flush(): void return; } - $fp = fsockopen('udp://'.$this->host, $this->port, $errno, $errstr, 1.0); + $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { return; @@ -66,7 +66,7 @@ public function flush(): void $level = error_reporting(0); foreach ($this->data as $line) { - fwrite($fp, $this->prefix.$line); + fwrite($fp, $this->prefix . $line); } error_reporting($level); diff --git a/src/Metrics/Collector/Telegraf.php b/src/Metrics/Collector/Telegraf.php index 01be2bc..ab3cdde 100644 --- a/src/Metrics/Collector/Telegraf.php +++ b/src/Metrics/Collector/Telegraf.php @@ -37,12 +37,12 @@ public function measure(string $variable, int $value, array $tags = []): void public function increment(string $variable, array $tags = []): void { - $this->data[] = $variable.$this->tags.':1|c'; + $this->data[] = $variable . $this->tags . ':1|c'; } public function decrement(string $variable, array $tags = []): void { - $this->data[] = $variable.$this->tags.':-1|c'; + $this->data[] = $variable . $this->tags . ':-1|c'; } public function timing(string $variable, int $time, array $tags = []): void @@ -66,7 +66,7 @@ public function flush(): void return; } - $fp = fsockopen('udp://'.$this->host, $this->port, $errno, $errstr, 1.0); + $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { return; @@ -74,7 +74,7 @@ public function flush(): void $level = error_reporting(0); foreach ($this->data as $line) { - fwrite($fp, $this->prefix.$line); + fwrite($fp, $this->prefix . $line); } error_reporting($level); @@ -87,6 +87,6 @@ public function flush(): void public function setTags(array $tags): void { $this->tags = http_build_query($tags, '', ','); - $this->tags = strlen($this->tags) > 0 ? ','.$this->tags : $this->tags; + $this->tags = \strlen($this->tags) > 0 ? ',' . $this->tags : $this->tags; } } diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 3d2a012..ac83675 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,17 +13,17 @@ namespace Beberlei\Metrics; -use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\DogStatsD; -use Beberlei\Metrics\Collector\Telegraf; use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; use Beberlei\Metrics\Collector\Librato; -use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\Logger; -use Beberlei\Metrics\Collector\InfluxDB; use Beberlei\Metrics\Collector\NullCollector; -use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; use Beberlei\Metrics\Collector\Prometheus; +use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\Telegraf; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -39,8 +39,9 @@ abstract class Factory * * @param string $type * - * @throws MetricsException * @return Collector\Collector + * + * @throws MetricsException */ public static function create($type, array $options = []) { diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 5d8c51f..63cd6d4 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -6,7 +6,6 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -18,33 +17,33 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('metrics.xml'); foreach ($config['collectors'] as $name => $colConfig) { $definition = $this->createCollector($colConfig['type'], $colConfig); - $container->setDefinition('beberlei_metrics.collector.'.$name, $definition); + $container->setDefinition('beberlei_metrics.collector.' . $name, $definition); } - if ($config['default'] && $container->hasDefinition('beberlei_metrics.collector.'.$config['default'])) { + if ($config['default'] && $container->hasDefinition('beberlei_metrics.collector.' . $config['default'])) { $name = $config['default']; - } elseif (1 !== count($config['collectors'])) { + } elseif (1 !== \count($config['collectors'])) { throw new \LogicException('You should select a default collector'); } - $container->setAlias('beberlei_metrics.collector', 'beberlei_metrics.collector.'.$name); + $container->setAlias('beberlei_metrics.collector', 'beberlei_metrics.collector.' . $name); $container->setAlias(Collector::class, 'beberlei_metrics.collector'); } private function createCollector($type, array $config) { - $definition = new ChildDefinition('beberlei_metrics.collector_proto.'.$config['type']); + $definition = new ChildDefinition('beberlei_metrics.collector_proto.' . $config['type']); // Theses listeners should be as late as possible $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'kernel.terminate']); $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'console.terminate']); - if (count($config['tags']) > 0) { + if (\count($config['tags']) > 0) { $definition->addMethodCall('setTags', [$config['tags']]); } diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index 4eb62cd..dba61d0 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -50,19 +50,19 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->validate() - ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['source'])) + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['source'])) ->thenInvalid('The source has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['username'])) + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['username'])) ->thenInvalid('The username has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(static fn($v): bool => 'librato' === $v['type'] && empty($v['password'])) + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['password'])) ->thenInvalid('The password has to be specified to use a Librato') ->end() ->validate() - ->ifTrue(static fn($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) + ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') ->end() ->end() diff --git a/tests/Metrics/Collector/InMemoryTest.php b/tests/Metrics/Collector/InMemoryTest.php index 259b8b0..8a04290 100644 --- a/tests/Metrics/Collector/InMemoryTest.php +++ b/tests/Metrics/Collector/InMemoryTest.php @@ -99,9 +99,9 @@ public function testIncrementingGauge(): void public function testSettingGaugeToNegativeValue(): void { - $this->collector->gauge(self::VARIABLE_A, 1); //sets to 1 - $this->collector->gauge(self::VARIABLE_A, 2); //sets to 2 - $this->collector->gauge(self::VARIABLE_A, -5); //decreases by 5 + $this->collector->gauge(self::VARIABLE_A, 1); // sets to 1 + $this->collector->gauge(self::VARIABLE_A, 2); // sets to 2 + $this->collector->gauge(self::VARIABLE_A, -5); // decreases by 5 $this->assertEquals(-3, $this->collector->getGauge(self::VARIABLE_A)); $this->collector->gauge(self::VARIABLE_A, 0); diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php index f976324..b05a6ab 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDBTest.php @@ -13,15 +13,14 @@ namespace Beberlei\Metrics\Tests\Collector; +use Beberlei\Metrics\Collector\InfluxDB; use InfluxDB\Client; use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject; -use Beberlei\Metrics\Collector\InfluxDB; class InfluxDBTest extends TestCase { /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject */ private $client; @@ -31,7 +30,8 @@ protected function setUp(): void { $this->client = $this->getMockBuilder(Client::class) ->disableOriginalConstructor() - ->getMock(); + ->getMock() + ; $this->collector = new InfluxDB($this->client); } @@ -41,7 +41,8 @@ public function testCollectIncrement(): void $this->client->expects($this->once()) ->method('mark') - ->with($expectedArgs); + ->with($expectedArgs) + ; $this->collector->increment('series-name'); $this->collector->flush(); @@ -53,7 +54,8 @@ public function testCollectDecrement(): void $this->client->expects($this->once()) ->method('mark') - ->with($expectedArgs); + ->with($expectedArgs) + ; $this->collector->decrement('series-name'); $this->collector->flush(); @@ -65,7 +67,8 @@ public function testCollectTiming(): void $this->client->expects($this->once()) ->method('mark') - ->with($expectedArgs); + ->with($expectedArgs) + ; $this->collector->timing('series-name', 47.11); $this->collector->flush(); @@ -77,7 +80,8 @@ public function testCollectMeasure(): void $this->client->expects($this->once()) ->method('mark') - ->with($expectedArgs); + ->with($expectedArgs) + ; $this->collector->measure('series-name', 47.11); $this->collector->flush(); @@ -91,7 +95,8 @@ public function testCollectMeasureWithTags(): void $this->client->expects($this->once()) ->method('mark') - ->with($expectedArgs); + ->with($expectedArgs) + ; $this->collector->setTags($expectedTags); $this->collector->measure('series-name', 47.11); diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index 895c258..8fd13e8 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -13,11 +13,11 @@ namespace Beberlei\Metrics\Tests\Collector; -use Prometheus\CollectorRegistry; -use Prometheus\Gauge; use Beberlei\Metrics\Collector\Prometheus; use PHPUnit\Framework\TestCase; +use Prometheus\CollectorRegistry; use Prometheus\Exception\MetricNotFoundException; +use Prometheus\Gauge; class PrometheusTest extends TestCase { diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index a1581a4..8eb27b4 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -2,22 +2,22 @@ namespace Beberlei\Metrics\Tests; -use Beberlei\Metrics\Collector\StatsD; +use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; use Beberlei\Metrics\Collector\Librato; -use Beberlei\Metrics\Collector\DoctrineDBAL; -use Doctrine\DBAL\Connection; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; -use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; -use Beberlei\Metrics\Collector\InfluxDB; -use InfluxDB\Client; use Beberlei\Metrics\Collector\Prometheus; -use Prometheus\CollectorRegistry; -use Beberlei\Metrics\MetricsException; +use Beberlei\Metrics\Collector\StatsD; use Beberlei\Metrics\Factory; +use Beberlei\Metrics\MetricsException; +use Doctrine\DBAL\Connection; +use InfluxDB\Client; use PHPUnit\Framework\TestCase; +use Prometheus\CollectorRegistry; use Psr\Log\NullLogger; class FactoryTest extends TestCase diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index c739b54..05e2bc8 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -13,23 +13,23 @@ namespace Beberlei\Bundle\MetricsBundle\Tests\DependencyInjection; +use Beberlei\Bundle\MetricsBundle\DependencyInjection\BeberleiMetricsExtension; +use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; +use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InMemory; use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; +use Beberlei\Metrics\Collector\Prometheus; use Beberlei\Metrics\Collector\StatsD; -use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Telegraf; use InfluxDB\Client; -use Beberlei\Metrics\Collector\InfluxDB; +use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; -use Beberlei\Metrics\Collector\Prometheus; -use Beberlei\Metrics\Collector\InMemory; use Psr\Log\NullLogger; -use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Beberlei\Bundle\MetricsBundle\DependencyInjection\BeberleiMetricsExtension; class BeberleiMetricsExtensionTest extends TestCase { @@ -228,6 +228,7 @@ public function testWithPrometheusAndWithTags(): void /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * * @expectedExceptionMessage The prometheus_collector_registry has to be specified to use a Prometheus */ public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified(): void @@ -248,7 +249,7 @@ private function createContainer($configs, array $publicServices = [], array $ad $container->set($serviceId, $additionalService); } - foreach($publicServices as $serviceId) { + foreach ($publicServices as $serviceId) { $container->getDefinition($serviceId)->setPublic(true); } diff --git a/tools/php-cs-fixer/.gitignore b/tools/php-cs-fixer/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/tools/php-cs-fixer/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/tools/php-cs-fixer/composer.json b/tools/php-cs-fixer/composer.json new file mode 100644 index 0000000..b0ac7ff --- /dev/null +++ b/tools/php-cs-fixer/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "friendsofphp/php-cs-fixer": "^3.46" + } +} diff --git a/tools/php-cs-fixer/composer.lock b/tools/php-cs-fixer/composer.lock new file mode 100644 index 0000000..811d41a --- /dev/null +++ b/tools/php-cs-fixer/composer.lock @@ -0,0 +1,1823 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "df2cc2d63945b734c8edd007837e47ad", + "packages": [ + { + "name": "composer/pcre", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-10-11T07:11:09+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.49.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8742f7aa6f72a399688b65e4f58992c2d4681fc2", + "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2", + "shasum": "" + }, + "require": { + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.0", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", + "phpunit/phpunit": "^9.6 || ^10.5.5", + "symfony/yaml": "^5.4 || ^6.0 || ^7.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", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.49.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-02-02T00:41:40+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T10:55:06+00:00" + }, + { + "name": "symfony/console", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c5010d50f1ee4b25cfa0201d9915cf1b14071456" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c5010d50f1ee4b25cfa0201d9915cf1b14071456", + "reference": "c5010d50f1ee4b25cfa0201d9915cf1b14071456", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e", + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.0.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": "2023-10-31T17:59:56+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "700ff4096e346f54cb628ea650767c8130f1001f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", + "reference": "700ff4096e346f54cb628ea650767c8130f1001f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.0.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": "2023-08-08T10:20:21+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "937a195147e0c27b2759ade834169ed006d0bc74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/937a195147e0c27b2759ade834169ed006d0bc74", + "reference": "937a195147e0c27b2759ade834169ed006d0bc74", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-26T14:02:43+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/string", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "524aac4a280b90a4420d8d6a040718d0586505ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/524aac4a280b90a4420d8d6a040718d0586505ac", + "reference": "524aac4a280b90a4420d8d6a040718d0586505ac", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T15:41:16+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} From ef3c16c63d1b1267ec6066720e034c7e75deedf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 19:52:05 +0100 Subject: [PATCH 13/32] chore: add phpstan --- phpstan-baseline.neon | 11 +++ phpstan.neon | 14 ++++ src/Metrics/Factory.php | 15 +--- .../BeberleiMetricsExtension.php | 7 +- .../DependencyInjection/Configuration.php | 12 +-- tools/phpstan/.gitignore | 1 + tools/phpstan/composer.json | 5 ++ tools/phpstan/composer.lock | 81 +++++++++++++++++++ 8 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon create mode 100644 tools/phpstan/.gitignore create mode 100644 tools/phpstan/composer.json create mode 100644 tools/phpstan/composer.lock diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..a5dbf7a --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Variable \\$name might not be defined\\.$#" + count: 1 + path: src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" + count: 1 + path: src/MetricsBundle/DependencyInjection/Configuration.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..cdf3e39 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,14 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: 8 + paths: + - src + inferPrivatePropertyTypeFromConstructor: true + checkGenericClassInNonGenericObjectType: false + ignoreErrors: + # Issue about array shape + - '{Property .* type has no value type specified in iterable type array\.}' + - '{Method .* has parameter \$\w+ with no value type specified in iterable type array\.}' + diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index ac83675..81bbf0b 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,6 +13,7 @@ namespace Beberlei\Metrics; +use Beberlei\Metrics\Collector\CollectorInterface; use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; @@ -27,23 +28,14 @@ use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; -/** - * Static factory for Metrics Collectors. - */ abstract class Factory { private static HttpClientInterface $httpClient; /** - * Create Metrics Collector Instance. - * - * @param string $type - * - * @return Collector\Collector - * * @throws MetricsException */ - public static function create($type, array $options = []) + public static function create(string $type, array $options = []): CollectorInterface { switch ($type) { case 'statsd': @@ -163,9 +155,6 @@ public static function create($type, array $options = []) case 'null': return new NullCollector(); - case 'null_inlinetaggable': - return new InlineTaggableGaugeableNullCollector(); - case 'prometheus': if (!isset($options['collector_registry'])) { throw new MetricsException("Missing 'collector_registry' key for Prometheus collector."); diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 63cd6d4..218ce14 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -3,6 +3,7 @@ namespace Beberlei\Bundle\MetricsBundle\DependencyInjection; use Beberlei\Metrics\Collector\Collector; +use Beberlei\Metrics\Collector\CollectorInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -14,7 +15,7 @@ class BeberleiMetricsExtension extends Extension { public function load(array $configs, ContainerBuilder $container): void { - $configuration = $this->getConfiguration($configs, $container); + $configuration = $this->getConfiguration($configs, $container) ?? throw new \LogicException('Expected configuration to be set'); $config = $this->processConfiguration($configuration, $configs); $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); @@ -32,10 +33,10 @@ public function load(array $configs, ContainerBuilder $container): void } $container->setAlias('beberlei_metrics.collector', 'beberlei_metrics.collector.' . $name); - $container->setAlias(Collector::class, 'beberlei_metrics.collector'); + $container->setAlias(CollectorInterface::class, 'beberlei_metrics.collector'); } - private function createCollector($type, array $config) + private function createCollector(string $type, array $config): ChildDefinition { $definition = new ChildDefinition('beberlei_metrics.collector_proto.' . $config['type']); diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index dba61d0..079cb25 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -14,14 +14,8 @@ class Configuration implements ConfigurationInterface */ public function getConfigTreeBuilder(): TreeBuilder { - $treeBuilder = new TreeBuilder('beberlei_metrics'); - if (method_exists($treeBuilder, 'getRootNode')) { - $rootNode = $treeBuilder->getRootNode(); - } else { - $rootNode = $treeBuilder->root('beberlei_metrics'); - } - - $rootNode + return (new TreeBuilder('beberlei_metrics')) + ->getRootNode() ->children() ->scalarNode('default') ->defaultNull() @@ -69,7 +63,5 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ; - - return $treeBuilder; } } diff --git a/tools/phpstan/.gitignore b/tools/phpstan/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/tools/phpstan/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/tools/phpstan/composer.json b/tools/phpstan/composer.json new file mode 100644 index 0000000..9cb4ed5 --- /dev/null +++ b/tools/phpstan/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "^1.10.55" + } +} diff --git a/tools/phpstan/composer.lock b/tools/phpstan/composer.lock new file mode 100644 index 0000000..84c7c5f --- /dev/null +++ b/tools/phpstan/composer.lock @@ -0,0 +1,81 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0df3d24799f35a5d623d94f4e4b6744e", + "packages": [ + { + "name": "phpstan/phpstan", + "version": "1.10.55", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9a88f9d18ddf4cf54c922fbeac16c4cb164c5949", + "reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2024-01-08T12:32:40+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} From dcd89cf7374d6579a0a572e3760aa651d8db9f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 22:07:54 +0100 Subject: [PATCH 14/32] chore: fixed test --- src/Metrics/Collector/DogStatsD.php | 2 +- .../Collector/GaugeableCollectorInterface.php | 2 +- src/Metrics/Collector/InMemory.php | 20 +++-- src/Metrics/Collector/Logger.php | 2 +- src/Metrics/Collector/NullCollector.php | 2 +- src/Metrics/Collector/StatsD.php | 2 +- src/Metrics/Collector/Telegraf.php | 2 +- src/Metrics/Factory.php | 1 - .../BeberleiMetricsExtension.php | 1 - .../DependencyInjection/Configuration.php | 83 ++++++++++--------- tests/Metrics/Collector/InMemoryTest.php | 6 +- tests/Metrics/Collector/InfluxDBTest.php | 12 +-- tests/Metrics/Collector/PrometheusTest.php | 20 ++--- tests/Metrics/FactoryTest.php | 40 +++++++-- .../BeberleiMetricsExtensionTest.php | 13 ++- 15 files changed, 121 insertions(+), 87 deletions(-) diff --git a/src/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php index bfc167d..7091d5e 100644 --- a/src/Metrics/Collector/DogStatsD.php +++ b/src/Metrics/Collector/DogStatsD.php @@ -44,7 +44,7 @@ public function timing(string $variable, int $time, array $tags = []): void $this->data[] = sprintf('%s:%s|ms%s', $variable, $time, $this->buildTagString($tags)); } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|g%s', $variable, $value, $this->buildTagString($tags)); } diff --git a/src/Metrics/Collector/GaugeableCollectorInterface.php b/src/Metrics/Collector/GaugeableCollectorInterface.php index 56938f9..3df4e28 100644 --- a/src/Metrics/Collector/GaugeableCollectorInterface.php +++ b/src/Metrics/Collector/GaugeableCollectorInterface.php @@ -18,5 +18,5 @@ interface GaugeableCollectorInterface /** * Updates a gauge by an arbitrary amount. */ - public function gauge(string $variable, int $value, array $tags = []): void; + public function gauge(string $variable, string|int $value, array $tags = []): void; } diff --git a/src/Metrics/Collector/InMemory.php b/src/Metrics/Collector/InMemory.php index 3b63564..c9d1771 100644 --- a/src/Metrics/Collector/InMemory.php +++ b/src/Metrics/Collector/InMemory.php @@ -46,16 +46,24 @@ public function decrement(string $variable, array $tags = []): void public function timing(string $variable, int $time, array $tags = []): void { - if (!isset($this->timingData[$variable])) { - $this->timingData[$variable] = 0; - } - + $this->timingData[$variable] ??= 0; $this->timingData[$variable] = $time; } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { - $this->gaugeData[$variable] = $value; + if (\is_int($value)) { + $this->gaugeData[$variable] = $value; + + return; + } + + $sign = substr($value, 0, 1); + if (!\in_array($sign, ['-', '+'], true)) { + throw new \InvalidArgumentException('Gauge value must be an integer or a string starting with + or -.'); + } + $this->gaugeData[$variable] ??= 0; + $this->gaugeData[$variable] += (int) $value; } public function flush(): void diff --git a/src/Metrics/Collector/Logger.php b/src/Metrics/Collector/Logger.php index 04593be..64a1159 100644 --- a/src/Metrics/Collector/Logger.php +++ b/src/Metrics/Collector/Logger.php @@ -42,7 +42,7 @@ public function timing(string $variable, int $time, array $tags = []): void $this->logger->debug(sprintf('timing:%s:%s', $variable, $time)); } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { $this->logger->debug(sprintf('gauge:%s:%s', $variable, $value)); } diff --git a/src/Metrics/Collector/NullCollector.php b/src/Metrics/Collector/NullCollector.php index 38f05c1..898c010 100644 --- a/src/Metrics/Collector/NullCollector.php +++ b/src/Metrics/Collector/NullCollector.php @@ -31,7 +31,7 @@ public function measure(string $variable, int $value, array $tags = []): void { } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { } diff --git a/src/Metrics/Collector/StatsD.php b/src/Metrics/Collector/StatsD.php index 5fc901f..26898b8 100644 --- a/src/Metrics/Collector/StatsD.php +++ b/src/Metrics/Collector/StatsD.php @@ -47,7 +47,7 @@ public function measure(string $variable, int $value, array $tags = []): void $this->data[] = sprintf('%s:%s|c', $variable, $value); } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { $this->data[] = sprintf('%s:%s|g', $variable, $value); } diff --git a/src/Metrics/Collector/Telegraf.php b/src/Metrics/Collector/Telegraf.php index ab3cdde..240b18b 100644 --- a/src/Metrics/Collector/Telegraf.php +++ b/src/Metrics/Collector/Telegraf.php @@ -50,7 +50,7 @@ public function timing(string $variable, int $time, array $tags = []): void $this->data[] = sprintf('%s%s:%s|ms', $variable, $this->tags, $time); } - public function gauge(string $variable, int $value, array $tags = []): void + public function gauge(string $variable, string|int $value, array $tags = []): void { $this->data[] = sprintf('%s%s:%s|g', $variable, $this->tags, $value); } diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 81bbf0b..3016ce3 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -18,7 +18,6 @@ use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; use Beberlei\Metrics\Collector\InfluxDB; -use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 218ce14..b9a209e 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -2,7 +2,6 @@ namespace Beberlei\Bundle\MetricsBundle\DependencyInjection; -use Beberlei\Metrics\Collector\Collector; use Beberlei\Metrics\Collector\CollectorInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index 079cb25..cbdf163 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -16,48 +16,49 @@ public function getConfigTreeBuilder(): TreeBuilder { return (new TreeBuilder('beberlei_metrics')) ->getRootNode() - ->children() - ->scalarNode('default') - ->defaultNull() - ->end() - ->arrayNode('collectors') - ->isRequired() - ->useAttributeAsKey('name') - ->prototype('array') - ->children() - ->scalarNode('type')->isRequired()->end() - ->scalarNode('connection')->defaultNull()->end() - ->scalarNode('file')->defaultNull()->end() - ->scalarNode('host')->defaultNull()->end() - ->scalarNode('password')->defaultNull()->end() - ->integerNode('port')->defaultNull()->end() - ->scalarNode('prefix')->defaultNull()->end() - ->scalarNode('protocol')->defaultNull()->end() - ->scalarNode('source')->defaultNull()->end() - ->scalarNode('username')->defaultNull()->end() - ->scalarNode('influxdb_client')->defaultNull()->end() - ->scalarNode('prometheus_collector_registry')->defaultNull()->info('It must to contain service id for Prometheus\\CollectorRegistry class instance.')->end() - ->scalarNode('namespace')->defaultValue('')->end() - ->arrayNode('tags') - ->defaultValue([]) - ->prototype('scalar')->end() + ->children() + ->scalarNode('default') + ->defaultNull() + ->end() + ->arrayNode('collectors') + ->isRequired() + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('type')->isRequired()->end() + ->scalarNode('connection')->defaultNull()->end() + ->scalarNode('file')->defaultNull()->end() + ->scalarNode('host')->defaultNull()->end() + ->scalarNode('password')->defaultNull()->end() + ->integerNode('port')->defaultNull()->end() + ->scalarNode('prefix')->defaultNull()->end() + ->scalarNode('protocol')->defaultNull()->end() + ->scalarNode('source')->defaultNull()->end() + ->scalarNode('username')->defaultNull()->end() + ->scalarNode('influxdb_client')->defaultNull()->end() + ->scalarNode('prometheus_collector_registry')->defaultNull()->info('It must to contain service id for Prometheus\\CollectorRegistry class instance.')->end() + ->scalarNode('namespace')->defaultValue('')->end() + ->arrayNode('tags') + ->defaultValue([]) + ->prototype('scalar')->end() + ->end() + ->end() + ->validate() + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['source'])) + ->thenInvalid('The source has to be specified to use a Librato') + ->end() + ->validate() + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['username'])) + ->thenInvalid('The username has to be specified to use a Librato') + ->end() + ->validate() + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['password'])) + ->thenInvalid('The password has to be specified to use a Librato') + ->end() + ->validate() + ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) + ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') ->end() - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['source'])) - ->thenInvalid('The source has to be specified to use a Librato') - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['username'])) - ->thenInvalid('The username has to be specified to use a Librato') - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['password'])) - ->thenInvalid('The password has to be specified to use a Librato') - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) - ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') ->end() ->end() ->end() diff --git a/tests/Metrics/Collector/InMemoryTest.php b/tests/Metrics/Collector/InMemoryTest.php index 8a04290..d24eb90 100644 --- a/tests/Metrics/Collector/InMemoryTest.php +++ b/tests/Metrics/Collector/InMemoryTest.php @@ -90,7 +90,7 @@ public function testSettingGauge(): void public function testIncrementingGauge(): void { - $this->collector->gauge(self::VARIABLE_A, '10'); + $this->collector->gauge(self::VARIABLE_A, 10); $this->collector->gauge(self::VARIABLE_A, '+2'); $this->collector->gauge(self::VARIABLE_A, '-3'); @@ -101,11 +101,11 @@ public function testSettingGaugeToNegativeValue(): void { $this->collector->gauge(self::VARIABLE_A, 1); // sets to 1 $this->collector->gauge(self::VARIABLE_A, 2); // sets to 2 - $this->collector->gauge(self::VARIABLE_A, -5); // decreases by 5 + $this->collector->gauge(self::VARIABLE_A, '-5'); // decreases by 5 $this->assertEquals(-3, $this->collector->getGauge(self::VARIABLE_A)); $this->collector->gauge(self::VARIABLE_A, 0); - $this->collector->gauge(self::VARIABLE_A, -5); + $this->collector->gauge(self::VARIABLE_A, '-5'); $this->assertEquals(-5, $this->collector->getGauge(self::VARIABLE_A)); } diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php index b05a6ab..4f38ac8 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDBTest.php @@ -63,27 +63,27 @@ public function testCollectDecrement(): void public function testCollectTiming(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => []]; + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') ->with($expectedArgs) ; - $this->collector->timing('series-name', 47.11); + $this->collector->timing('series-name', 47); $this->collector->flush(); } public function testCollectMeasure(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => []]; + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => []]; $this->client->expects($this->once()) ->method('mark') ->with($expectedArgs) ; - $this->collector->measure('series-name', 47.11); + $this->collector->measure('series-name', 47); $this->collector->flush(); } @@ -91,7 +91,7 @@ public function testCollectMeasureWithTags(): void { $expectedTags = ['dc' => 'west', 'node' => 'nemesis101']; - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47.11]]], 'tags' => $expectedTags]; + $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => $expectedTags]; $this->client->expects($this->once()) ->method('mark') @@ -99,7 +99,7 @@ public function testCollectMeasureWithTags(): void ; $this->collector->setTags($expectedTags); - $this->collector->measure('series-name', 47.11); + $this->collector->measure('series-name', 47); $this->collector->flush(); } } diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index 8fd13e8..7a3a945 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -14,6 +14,7 @@ namespace Beberlei\Metrics\Tests\Collector; use Beberlei\Metrics\Collector\Prometheus; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; use Prometheus\Exception\MetricNotFoundException; @@ -25,10 +26,7 @@ class PrometheusTest extends TestCase public const TEST_VARIABLE_NAME = 'some_variable_name'; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $collectorRegistryMock; + private MockObject&CollectorRegistry $collectorRegistryMock; private Prometheus $collector; @@ -296,14 +294,14 @@ public function testFlushWhenCallsTwiceWithDifferentData(): void ->getMock() ; $gaugeMock - ->expects($this->at(0)) - ->method('set') - ->with($firstExpectedVariableValue, []) - ; - $gaugeMock - ->expects($this->at(1)) + ->expects($matcher = $this->exactly(2)) ->method('set') - ->with($secondExpectedVariableValue, []) + ->willReturnCallback(function ($value) use ($matcher) { + match ($matcher->getInvocationCount()) { + 1 => $this->assertEquals(123, $value), + 2 => $this->assertEquals(321, $value), + }; + }) ; $this->collectorRegistryMock diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index 8eb27b4..607e16e 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -6,7 +6,6 @@ use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; use Beberlei\Metrics\Collector\InfluxDB; -use Beberlei\Metrics\Collector\InlineTaggableGaugeableNullCollector; use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; @@ -22,9 +21,25 @@ class FactoryTest extends TestCase { - public function getCreateValidMetricTests(): array + public function getCreateValidMetricTests(): iterable { - return [[StatsD::class, 'statsd'], [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']], [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234]], [StatsD::class, 'statsd', ['host' => 'localhost']], [DogStatsD::class, 'dogstatsd'], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234]], [DogStatsD::class, 'dogstatsd', ['host' => 'localhost']], [Graphite::class, 'graphite'], [Graphite::class, 'graphite', ['host' => 'localhost', 'port' => 1234]], [Librato::class, 'librato', ['hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password']], [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]], [Logger::class, 'logger', ['logger' => new NullLogger()]], [NullCollector::class, 'null'], [InlineTaggableGaugeableNullCollector::class, 'null_inlinetaggable'], [InfluxDB::class, 'influxdb', ['client' => $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock()]], [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock()]], [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace']]]; + yield [StatsD::class, 'statsd']; + yield [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']]; + yield [StatsD::class, 'statsd', ['host' => 'localhost', 'port' => 1234]]; + yield [StatsD::class, 'statsd', ['host' => 'localhost']]; + yield [DogStatsD::class, 'dogstatsd']; + yield [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234, 'prefix' => 'prefix']]; + yield [DogStatsD::class, 'dogstatsd', ['host' => 'localhost', 'port' => 1234]]; + yield [DogStatsD::class, 'dogstatsd', ['host' => 'localhost']]; + yield [Graphite::class, 'graphite']; + yield [Graphite::class, 'graphite', ['host' => 'localhost', 'port' => 1234]]; + yield [Librato::class, 'librato', ['hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password']]; + yield [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]]; + yield [Logger::class, 'logger', ['logger' => new NullLogger()]]; + yield [NullCollector::class, 'null']; + yield [InfluxDB::class, 'influxdb', ['client' => $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock()]]; + yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock()]]; + yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace']]; } /** @@ -35,9 +50,24 @@ public function testCreateValidMetric(string $expectedClass, string $type, array $this->assertInstanceOf($expectedClass, Factory::create($type, $options)); } - public function getCreateThrowExceptionIfOptionsAreInvalidTests(): array + public function getCreateThrowExceptionIfOptionsAreInvalidTests(): iterable { - return [['You should specified a host if you specified a port.', 'statsd', ['port' => '1234']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['port' => '1234', 'prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'statsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']], ['You should specified a host if you specified a port.', 'dogstatsd', ['port' => '1234']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['port' => '1234', 'prefix' => 'prefix']], ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']], ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']], ['Hostname is required for librato collector.', 'librato'], ['No username given for librato collector.', 'librato', ['hostname' => 'foobar.com']], ['No password given for librato collector.', 'librato', ['hostname' => 'foobar.com', 'username' => 'username']], ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal'], ["Missing 'logger' key with logger service.", 'logger'], ["Missing 'client' key for InfluxDB collector.", 'influxdb'], ["Missing 'collector_registry' key for Prometheus collector.", 'prometheus']]; + yield ['You should specified a host if you specified a port.', 'statsd', ['port' => '1234']]; + yield ['You should specified a host and a port if you specified a prefix.', 'statsd', ['prefix' => 'prefix']]; + yield ['You should specified a host and a port if you specified a prefix.', 'statsd', ['port' => '1234', 'prefix' => 'prefix']]; + yield ['You should specified a host and a port if you specified a prefix.', 'statsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']]; + yield ['You should specified a host if you specified a port.', 'dogstatsd', ['port' => '1234']]; + yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['prefix' => 'prefix']]; + yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['port' => '1234', 'prefix' => 'prefix']]; + yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']]; + yield ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']]; + yield ['Hostname is required for librato collector.', 'librato']; + yield ['No username given for librato collector.', 'librato', ['hostname' => 'foobar.com']]; + yield ['No password given for librato collector.', 'librato', ['hostname' => 'foobar.com', 'username' => 'username']]; + yield ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal']; + yield ["Missing 'logger' key with logger service.", 'logger']; + yield ["Missing 'client' key for InfluxDB collector.", 'influxdb']; + yield ["Missing 'collector_registry' key for Prometheus collector.", 'prometheus']; } /** diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index 05e2bc8..554730d 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -28,6 +28,7 @@ use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; use Psr\Log\NullLogger; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -51,11 +52,12 @@ public function testWithGraphite(): void } /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * The source has to be specified to use a Librato + * The source has to be specified to use a Librato. */ public function testWithLibratoAndInvalidConfiguration(): void { + $this->expectException(InvalidConfigurationException::class); + $container = $this->createContainer(['collectors' => ['simple' => ['type' => 'librato']]], ['beberlei_metrics.collector.librato']); $this->assertInstanceOf(Librato::class, $container->get('beberlei_metrics.collector.librato')); @@ -226,13 +228,10 @@ public function testWithPrometheusAndWithTags(): void $this->assertEquals($expectedTags, $this->getProperty($collector, 'tags')); } - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * - * @expectedExceptionMessage The prometheus_collector_registry has to be specified to use a Prometheus - */ public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified(): void { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The prometheus_collector_registry has to be specified to use a Prometheus'); $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]]); } From b0d856d1c6f1573eac523a8064e509e1fab3588e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 22:23:24 +0100 Subject: [PATCH 15/32] composer: fix some constraints --- composer.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 83e700f..faf7084 100644 --- a/composer.json +++ b/composer.json @@ -21,15 +21,17 @@ "require-dev": { "php": ">=8.1", "corley/influxdb-sdk": "^0.5.1", - "doctrine/dbal": "~2.0", - "jimdo/prometheus_client_php": "^0.5", + "doctrine/dbal": "^2.0", + "jimdo/prometheus_client_php": "^0.5.1", "symfony/config": "^5.4 || ^6.4 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", "symfony/http-client": "^5.4 || ^6.4 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", - "symfony/phpunit-bridge": "^6.4 || ^7.0" + "symfony/phpunit-bridge": "^6.4.4 || ^7.0" }, "conflict": { + "doctrine/dbal": "<2", + "jimdo/prometheus_client_php": "<0.5.1", "symfony/config": "<5.4", "symfony/dependency-injection": "<5.4", "symfony/http-client": "<5.4", From 1ed9ab79de51452296b1f8bc3245227d66da2d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 22:13:56 +0100 Subject: [PATCH 16/32] ci: add GHA --- .github/workflows/ci.yaml | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..22159d7 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,66 @@ +name: CI + +on: + push: + # FIXME: Change the branch name to your main branch + branches: [main] + pull_request: ~ + +jobs: + cs: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: PHP-CS-Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --diff --dry-run + + phpstan: + name: PHPStan + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Composer dependencies + run: composer update --prefer-dist --no-interaction + + - name: PHPStan + env: + CHECK_PLATFORM_REQUIREMENTS: false + REQUIRE_DEV: true + uses: docker://oskarstark/phpstan-ga + + phpunit: + name: PHPUnit on PHP ${{ matrix.php-version }} ${{ matrix.name }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: ["8.2", "8.3"] + composer-flags: [""] + name: [""] + include: + - php-version: 8.1 + composer-flags: "--prefer-lowest" + name: "(prefer lowest dependencies)" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, xml + ini-values: phar.readonly="Off" + + - name: Install Composer dependencies + run: composer update --prefer-dist --no-interaction ${{ matrix.composer-flags }} + + - name: Run Tests + run: vendor/bin/simple-phpunit From d59d77b9eb2c33c2a09adf043e25ebc8fd7fc3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 22:37:52 +0100 Subject: [PATCH 17/32] chore: ensure Collector cannot raise error or exception --- src/Metrics/Collector/DogStatsD.php | 10 +++++++--- src/Metrics/Collector/Graphite.php | 24 ++++++++++++++---------- src/Metrics/Collector/InfluxDB.php | 6 +++++- src/Metrics/Collector/StatsD.php | 10 +++++++--- src/Metrics/Collector/Telegraf.php | 11 ++++++++--- src/Metrics/Utils/Box.php | 19 +++++++++++++++++++ 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 src/Metrics/Utils/Box.php diff --git a/src/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php index 7091d5e..0604ecf 100644 --- a/src/Metrics/Collector/DogStatsD.php +++ b/src/Metrics/Collector/DogStatsD.php @@ -13,6 +13,8 @@ namespace Beberlei\Metrics\Collector; +use Beberlei\Metrics\Utils\Box; + class DogStatsD implements CollectorInterface, GaugeableCollectorInterface { private array $data = []; @@ -54,6 +56,11 @@ public function flush(): void if (!$this->data) { return; } + Box::box($this->doFlush(...)); + } + + private function doFlush(): void + { $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); @@ -61,13 +68,10 @@ public function flush(): void return; } - $level = error_reporting(0); foreach ($this->data as $line) { fwrite($fp, $this->prefix . $line); } - error_reporting($level); - fclose($fp); $this->data = []; diff --git a/src/Metrics/Collector/Graphite.php b/src/Metrics/Collector/Graphite.php index 1bc7711..9ff5930 100644 --- a/src/Metrics/Collector/Graphite.php +++ b/src/Metrics/Collector/Graphite.php @@ -13,6 +13,8 @@ namespace Beberlei\Metrics\Collector; +use Beberlei\Metrics\Utils\Box; + /** * Sends statistics to the stats daemon over UDP or TCP. */ @@ -53,21 +55,23 @@ public function flush(): void return; } - try { - $fp = fsockopen($this->protocol . '://' . $this->host, $this->port); + Box::box($this->doFlush(...)); + } - if (!$fp) { - return; - } + private function doFlush(): void + { + $fp = fsockopen($this->protocol . '://' . $this->host, $this->port); - foreach ($this->data as $line) { - fwrite($fp, (string) $line); - } + if (!$fp) { + return; + } - fclose($fp); - } catch (\Exception) { + foreach ($this->data as $line) { + fwrite($fp, (string) $line); } + fclose($fp); + $this->data = []; } diff --git a/src/Metrics/Collector/InfluxDB.php b/src/Metrics/Collector/InfluxDB.php index e995972..75ba748 100644 --- a/src/Metrics/Collector/InfluxDB.php +++ b/src/Metrics/Collector/InfluxDB.php @@ -48,7 +48,11 @@ public function timing(string $variable, int $time, array $tags = []): void public function flush(): void { foreach ($this->data as $data) { - $this->client->mark(['points' => [['measurement' => $data[0], 'fields' => ['value' => $data[1]]]], 'tags' => $data[2] + $this->tags]); + try { + $this->client->mark(['points' => [['measurement' => $data[0], 'fields' => ['value' => $data[1]]]], 'tags' => $data[2] + $this->tags]); + } catch (\Exception) { + continue; + } } $this->data = []; diff --git a/src/Metrics/Collector/StatsD.php b/src/Metrics/Collector/StatsD.php index 26898b8..8e9f5b5 100644 --- a/src/Metrics/Collector/StatsD.php +++ b/src/Metrics/Collector/StatsD.php @@ -13,6 +13,8 @@ namespace Beberlei\Metrics\Collector; +use Beberlei\Metrics\Utils\Box; + /** * Sends statistics to the stats daemon over UDP. */ @@ -58,19 +60,21 @@ public function flush(): void return; } + Box::box($this->doFlush(...)); + } + + private function doFlush(): void + { $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { return; } - $level = error_reporting(0); foreach ($this->data as $line) { fwrite($fp, $this->prefix . $line); } - error_reporting($level); - fclose($fp); $this->data = []; diff --git a/src/Metrics/Collector/Telegraf.php b/src/Metrics/Collector/Telegraf.php index 240b18b..e2b2905 100644 --- a/src/Metrics/Collector/Telegraf.php +++ b/src/Metrics/Collector/Telegraf.php @@ -13,6 +13,8 @@ namespace Beberlei\Metrics\Collector; +use Beberlei\Metrics\Utils\Box; + /** * Sends statistics to the StatsD daemon over UDP, * ad hoc implementation for the StatsD - Telegraf integration, @@ -66,19 +68,22 @@ public function flush(): void return; } + Box::box($this->doFlush(...)); + } + + private function doFlush(): void + { + $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { return; } - $level = error_reporting(0); foreach ($this->data as $line) { fwrite($fp, $this->prefix . $line); } - error_reporting($level); - fclose($fp); $this->data = []; diff --git a/src/Metrics/Utils/Box.php b/src/Metrics/Utils/Box.php new file mode 100644 index 0000000..b191fe5 --- /dev/null +++ b/src/Metrics/Utils/Box.php @@ -0,0 +1,19 @@ + null); + + return $callable(); + } catch (\Throwable) { + // ignore + } finally { + restore_error_handler(); + } + } +} From cc5cc9a1e66308e1ce56f88f33f4ceceb5153ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 23:05:51 +0100 Subject: [PATCH 18/32] example: init blank symfony application --- example/statsd.php | 13 - examples/.castor/docker.php | 472 + examples/.castor/qa.php | 51 + examples/.env | 41 + examples/.env.test | 6 + examples/.gitattributes | 2 + examples/.gitignore | 30 + examples/.php-cs-fixer.php | 27 + examples/README.md | 102 + examples/assets/app.js | 10 + examples/assets/bootstrap.js | 5 + examples/assets/controllers.json | 15 + .../assets/controllers/hello_controller.js | 16 + examples/assets/styles/app.css | 3 + examples/bin/console | 17 + examples/bin/phpunit | 23 + examples/castor.php | 81 + examples/composer.json | 108 + examples/composer.lock | 9702 +++++++++++++++++ examples/config/bundles.php | 16 + examples/config/packages/asset_mapper.yaml | 5 + examples/config/packages/cache.yaml | 19 + examples/config/packages/debug.yaml | 5 + examples/config/packages/doctrine.yaml | 50 + .../config/packages/doctrine_migrations.yaml | 6 + examples/config/packages/framework.yaml | 16 + examples/config/packages/mailer.yaml | 3 + examples/config/packages/messenger.yaml | 24 + examples/config/packages/monolog.yaml | 62 + examples/config/packages/notifier.yaml | 12 + examples/config/packages/routing.yaml | 10 + examples/config/packages/security.yaml | 39 + examples/config/packages/translation.yaml | 7 + examples/config/packages/twig.yaml | 6 + examples/config/packages/validator.yaml | 11 + examples/config/packages/web_profiler.yaml | 17 + examples/config/preload.php | 5 + examples/config/routes.yaml | 5 + examples/config/routes/framework.yaml | 4 + examples/config/routes/security.yaml | 3 + examples/config/routes/web_profiler.yaml | 8 + examples/config/services.yaml | 24 + examples/importmap.php | 28 + .../docker/docker-compose.builder.yml | 25 + .../docker/docker-compose.docker-for-x.yml | 13 + .../docker/docker-compose.worker.yml | 20 + .../infrastructure/docker/docker-compose.yml | 38 + .../docker/services/php/Dockerfile | 121 + .../mods-available/app-default.ini | 27 + .../services/php/builder/etc/sudoers.d/sudo | 1 + .../mods-available/app-builder.ini | 5 + .../docker/services/php/entrypoint | 26 + .../php/frontend/etc/nginx/environments | 0 .../php/frontend/etc/nginx/nginx.conf | 74 + .../php/frontend/etc/service/nginx/run | 3 + .../php/frontend/etc/service/php-fpm/run | 3 + .../php-configuration/fpm/php-fpm.conf | 19 + .../mods-available/app-fpm.ini | 5 + .../docker/services/router/Dockerfile | 5 + .../docker/services/router/certs/.gitkeep | 0 .../docker/services/router/generate-ssl.sh | 16 + .../docker/services/router/openssl.cnf | 19 + .../services/router/traefik/dynamic_conf.yaml | 12 + .../services/router/traefik/traefik.yaml | 28 + examples/migrations/.gitignore | 0 examples/phpstan.neon | 31 + examples/phpunit.xml.dist | 38 + examples/public/index.php | 9 + examples/src/Controller/.gitignore | 0 .../src/Controller/HomepageController.php | 28 + examples/src/Entity/.gitignore | 0 examples/src/Kernel.php | 11 + examples/src/Repository/.gitignore | 0 examples/symfony.lock | 282 + examples/templates/base.html.twig | 17 + examples/tests/bootstrap.php | 11 + examples/tools/bin/php-cs-fixer | 1 + examples/tools/bin/phpstan | 1 + examples/tools/php-cs-fixer/.gitignore | 1 + examples/tools/php-cs-fixer/composer.json | 12 + examples/tools/php-cs-fixer/composer.lock | 1845 ++++ examples/tools/phpstan/.gitignore | 2 + examples/tools/phpstan/composer.json | 17 + examples/tools/phpstan/composer.lock | 200 + examples/translations/.gitignore | 0 85 files changed, 14062 insertions(+), 13 deletions(-) delete mode 100644 example/statsd.php create mode 100644 examples/.castor/docker.php create mode 100644 examples/.castor/qa.php create mode 100644 examples/.env create mode 100644 examples/.env.test create mode 100644 examples/.gitattributes create mode 100644 examples/.gitignore create mode 100644 examples/.php-cs-fixer.php create mode 100644 examples/README.md create mode 100644 examples/assets/app.js create mode 100644 examples/assets/bootstrap.js create mode 100644 examples/assets/controllers.json create mode 100644 examples/assets/controllers/hello_controller.js create mode 100644 examples/assets/styles/app.css create mode 100755 examples/bin/console create mode 100755 examples/bin/phpunit create mode 100644 examples/castor.php create mode 100644 examples/composer.json create mode 100644 examples/composer.lock create mode 100644 examples/config/bundles.php create mode 100644 examples/config/packages/asset_mapper.yaml create mode 100644 examples/config/packages/cache.yaml create mode 100644 examples/config/packages/debug.yaml create mode 100644 examples/config/packages/doctrine.yaml create mode 100644 examples/config/packages/doctrine_migrations.yaml create mode 100644 examples/config/packages/framework.yaml create mode 100644 examples/config/packages/mailer.yaml create mode 100644 examples/config/packages/messenger.yaml create mode 100644 examples/config/packages/monolog.yaml create mode 100644 examples/config/packages/notifier.yaml create mode 100644 examples/config/packages/routing.yaml create mode 100644 examples/config/packages/security.yaml create mode 100644 examples/config/packages/translation.yaml create mode 100644 examples/config/packages/twig.yaml create mode 100644 examples/config/packages/validator.yaml create mode 100644 examples/config/packages/web_profiler.yaml create mode 100644 examples/config/preload.php create mode 100644 examples/config/routes.yaml create mode 100644 examples/config/routes/framework.yaml create mode 100644 examples/config/routes/security.yaml create mode 100644 examples/config/routes/web_profiler.yaml create mode 100644 examples/config/services.yaml create mode 100644 examples/importmap.php create mode 100644 examples/infrastructure/docker/docker-compose.builder.yml create mode 100644 examples/infrastructure/docker/docker-compose.docker-for-x.yml create mode 100644 examples/infrastructure/docker/docker-compose.worker.yml create mode 100644 examples/infrastructure/docker/docker-compose.yml create mode 100644 examples/infrastructure/docker/services/php/Dockerfile create mode 100644 examples/infrastructure/docker/services/php/base/php-configuration/mods-available/app-default.ini create mode 100644 examples/infrastructure/docker/services/php/builder/etc/sudoers.d/sudo create mode 100644 examples/infrastructure/docker/services/php/builder/php-configuration/mods-available/app-builder.ini create mode 100755 examples/infrastructure/docker/services/php/entrypoint create mode 100644 examples/infrastructure/docker/services/php/frontend/etc/nginx/environments create mode 100644 examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf create mode 100755 examples/infrastructure/docker/services/php/frontend/etc/service/nginx/run create mode 100755 examples/infrastructure/docker/services/php/frontend/etc/service/php-fpm/run create mode 100644 examples/infrastructure/docker/services/php/frontend/php-configuration/fpm/php-fpm.conf create mode 100644 examples/infrastructure/docker/services/php/frontend/php-configuration/mods-available/app-fpm.ini create mode 100644 examples/infrastructure/docker/services/router/Dockerfile create mode 100644 examples/infrastructure/docker/services/router/certs/.gitkeep create mode 100755 examples/infrastructure/docker/services/router/generate-ssl.sh create mode 100644 examples/infrastructure/docker/services/router/openssl.cnf create mode 100644 examples/infrastructure/docker/services/router/traefik/dynamic_conf.yaml create mode 100644 examples/infrastructure/docker/services/router/traefik/traefik.yaml create mode 100644 examples/migrations/.gitignore create mode 100644 examples/phpstan.neon create mode 100644 examples/phpunit.xml.dist create mode 100644 examples/public/index.php create mode 100644 examples/src/Controller/.gitignore create mode 100644 examples/src/Controller/HomepageController.php create mode 100644 examples/src/Entity/.gitignore create mode 100644 examples/src/Kernel.php create mode 100644 examples/src/Repository/.gitignore create mode 100644 examples/symfony.lock create mode 100644 examples/templates/base.html.twig create mode 100644 examples/tests/bootstrap.php create mode 120000 examples/tools/bin/php-cs-fixer create mode 120000 examples/tools/bin/phpstan create mode 100644 examples/tools/php-cs-fixer/.gitignore create mode 100644 examples/tools/php-cs-fixer/composer.json create mode 100644 examples/tools/php-cs-fixer/composer.lock create mode 100644 examples/tools/phpstan/.gitignore create mode 100644 examples/tools/phpstan/composer.json create mode 100644 examples/tools/phpstan/composer.lock create mode 100644 examples/translations/.gitignore diff --git a/example/statsd.php b/example/statsd.php deleted file mode 100644 index 8ce6cc5..0000000 --- a/example/statsd.php +++ /dev/null @@ -1,13 +0,0 @@ -increment('foo.bar'); - $metrics->decrement('foo.baz'); - $metrics->measure('foo', rand(1, 10)); - $metrics->flush(); - usleep(500); -} diff --git a/examples/.castor/docker.php b/examples/.castor/docker.php new file mode 100644 index 0000000..3640ec7 --- /dev/null +++ b/examples/.castor/docker.php @@ -0,0 +1,472 @@ +title('About this project'); + + io()->comment('Run castor to display all available commands.'); + io()->comment('Run castor about to display this project help.'); + io()->comment('Run castor help [command] to display Castor help.'); + + io()->section('Available URLs for this project:'); + $urls = [variable('root_domain'), ...variable('extra_domains')]; + + $payload = @file_get_contents(sprintf('http://%s:8080/api/http/routers', variable('root_domain'))); + if ($payload) { + $routers = json_decode($payload, true); + $projectName = variable('project_name'); + foreach ($routers as $router) { + if (!preg_match("{^{$projectName}-(.*)@docker$}", $router['name'])) { + continue; + } + if ("frontend-{$projectName}" === $router['service']) { + continue; + } + if (!preg_match('{^Host\\(`(?P.*)`\\)$}', $router['rule'], $matches)) { + continue; + } + $hosts = explode('`) || Host(`', $matches['hosts']); + $urls = [...$urls, ...$hosts]; + } + } + io()->listing(array_map(fn ($url) => "https://{$url}", $urls)); +} + +#[AsTask(description: 'Opens the project in your browser', namespace: '')] +function open(): void +{ + run(['open', 'https://' . variable('root_domain')], quiet: true); +} + +#[AsTask(description: 'Builds the infrastructure', aliases: ['build'])] +function build(): void +{ + io()->title('Building infrastructure'); + + $userId = variable('user_id'); + $phpVersion = variable('php_version'); + + $command = [ + 'build', + '--build-arg', "USER_ID={$userId}", + '--build-arg', "PHP_VERSION={$phpVersion}", + ]; + + docker_compose($command, withBuilder: true); +} + +#[AsTask(description: 'Builds and starts the infrastructure', aliases: ['up'])] +function up(): void +{ + io()->title('Starting infrastructure'); + + try { + docker_compose(['up', '--detach', '--no-build']); + } catch (ExceptionInterface $e) { + io()->error('An error occured while starting the infrastructure.'); + io()->note('Did you forget to run "castor docker:build"?'); + io()->note('Or you forget to login to the registry?'); + + throw $e; + } +} + +#[AsTask(description: 'Stops the infrastructure', aliases: ['stop'])] +function stop(): void +{ + io()->title('Stopping infrastructure'); + + docker_compose(['stop']); +} + +#[AsTask(description: 'Opens a shell (bash) into a builder container', aliases: ['builder'])] +function builder(): void +{ + $c = context() + ->withTimeout(null) + ->withTty() + ->withEnvironment($_ENV + $_SERVER) + ->withAllowFailure() + ; + docker_compose_run('bash', c: $c); +} + +#[AsTask(description: 'Displays infrastructure logs', aliases: ['logs'])] +function logs(): void +{ + docker_compose(['logs', '-f', '--tail', '150'], c: context()->withTty()); +} + +#[AsTask(description: 'Lists containers status', aliases: ['ps'])] +function ps(): void +{ + docker_compose(['ps'], withBuilder: false); +} + +#[AsTask(description: 'Cleans the infrastructure (remove container, volume, networks)', aliases: ['destroy'])] +function destroy( + #[AsOption(description: 'Force the destruction without confirmation', shortcut: 'f')] + bool $force = false, +): void { + io()->title('Destroying infrastructure'); + + if (!$force) { + io()->warning('This will permanently remove all containers, volumes, networks... created for this project.'); + io()->note('You can use the --force option to avoid this confirmation.'); + if (!io()->confirm('Are you sure?', false)) { + io()->comment('Aborted.'); + + return; + } + } + + docker_compose(['down', '--remove-orphans', '--volumes', '--rmi=local'], withBuilder: true); + $files = finder() + ->in(variable('root_dir') . '/infrastructure/docker/services/router/certs/') + ->name('*.pem') + ->files() + ; + fs()->remove($files); +} + +#[AsTask(description: 'Generates SSL certificates (with mkcert if available or self-signed if not)', namespace: '')] +function generate_certificates( + #[AsOption(description: 'Force the certificates re-generation without confirmation', shortcut: 'f')] + bool $force = false, +): void { + $sslDir = variable('root_dir') . '/infrastructure/docker/services/router/certs'; + + if (file_exists("{$sslDir}/cert.pem") && !$force) { + io()->comment('SSL certificates already exists.'); + io()->note('Run "castor docker:generate-certificates --force" to generate new certificates.'); + + return; + } + + io()->title('Generating SSL certificates'); + + if ($force) { + if (file_exists($f = "{$sslDir}/cert.pem")) { + io()->comment('Removing existing certificates in infrastructure/docker/services/router/certs/*.pem.'); + unlink($f); + } + + if (file_exists($f = "{$sslDir}/key.pem")) { + unlink($f); + } + } + + $finder = new ExecutableFinder(); + $mkcert = $finder->find('mkcert'); + + if ($mkcert) { + $pathCaRoot = capture(['mkcert', '-CAROOT']); + + if (!is_dir($pathCaRoot)) { + io()->warning('You must have mkcert CA Root installed on your host with "mkcert -install" command.'); + + return; + } + + $rootDomain = variable('root_domain'); + + run([ + 'mkcert', + '-cert-file', "{$sslDir}/cert.pem", + '-key-file', "{$sslDir}/key.pem", + $rootDomain, + "*.{$rootDomain}", + ...variable('extra_domains'), + ]); + + io()->success('Successfully generated SSL certificates with mkcert.'); + + if ($force) { + io()->note('Please restart the infrastructure to use the new certificates with "castor up" or "castor start".'); + } + + return; + } + + run(['infrastructure/docker/services/router/generate-ssl.sh'], quiet: true); + + io()->success('Successfully generated self-signed SSL certificates in infrastructure/docker/services/router/certs/*.pem.'); + io()->comment('Consider installing mkcert to generate locally trusted SSL certificates and run "castor docker:generate-certificates --force".'); + + if ($force) { + io()->note('Please restart the infrastructure to use the new certificates with "castor up" or "castor start".'); + } +} + +#[AsTask(description: 'Starts the workers', namespace: 'docker:worker', name: 'start', aliases: ['start-workers'])] +function workers_start(): void +{ + io()->title('Starting workers'); + + $workers = get_workers(); + + if (!$workers) { + return; + } + + run([ + 'docker', + 'update', + '--restart=unless-stopped', + ...$workers, + ], quiet: true); + + run([ + 'docker', + 'start', + ...$workers, + ], quiet: true); +} + +#[AsTask(description: 'Stops the workers', namespace: 'docker:worker', name: 'stop', aliases: ['stop-workers'])] +function workers_stop(): void +{ + io()->title('Stopping workers'); + + $workers = get_workers(); + + if (!$workers) { + return; + } + + run([ + 'docker', + 'update', + '--restart=no', + ...$workers, + ]); + + run([ + 'docker', + 'stop', + ...$workers, + ]); +} + +#[AsContext(default: true)] +function create_default_context(): Context +{ + $data = create_default_variables() + [ + 'project_name' => 'app', + 'root_domain' => 'app.test', + 'extra_domains' => [], + 'php_version' => '8.3', + 'docker_compose_files' => [ + 'docker-compose.yml', + 'docker-compose.worker.yml', + ], + 'macos' => false, + 'power_shell' => false, + 'user_id' => posix_geteuid(), + 'root_dir' => \dirname(__DIR__), + 'env' => $_SERVER['CI'] ?? false ? 'ci' : 'dev', + ]; + + if (file_exists($data['root_dir'] . '/infrastructure/docker/docker-compose.override.yml')) { + $data['docker_compose_files'][] = 'docker-compose.override.yml'; + } + + // We need an empty context to run command, since the default context has + // not been set in castor, since we ARE creating it right now + $emptyContext = new Context(); + + $data['composer_cache_dir'] = cache('composer_cache_dir', function () use ($emptyContext): string { + $composerCacheDir = capture(['composer', 'global', 'config', 'cache-dir', '-q'], onFailure: '', context: $emptyContext); + // If PHP is broken, the output will not be a valid path but an error message + if (!is_dir($composerCacheDir)) { + $composerCacheDir = sys_get_temp_dir() . '/castor/composer'; + // If the directory does not exist, we create it. Otherwise, docker + // will do, as root, and the user will not be able to write in it. + if (!is_dir($composerCacheDir)) { + mkdir($composerCacheDir, 0777, true); + } + } + + return $composerCacheDir; + }); + + $platform = strtolower(php_uname('s')); + if (str_contains($platform, 'darwin')) { + $data['macos'] = true; + $data['docker_compose_files'][] = 'docker-compose.docker-for-x.yml'; + } elseif (\in_array($platform, ['win32', 'win64'])) { + $data['docker_compose_files'][] = 'docker-compose.docker-for-x.yml'; + $data['power_shell'] = true; + } + + if ($data['user_id'] > 256000) { + $data['user_id'] = 1000; + } + + if (0 === $data['user_id']) { + log('Running as root? Fallback to fake user id.', 'warning'); + $data['user_id'] = 1000; + } + + return new Context($data, pty: 'dev' === $data['env']); +} + +/** + * @param array $subCommand + */ +function docker_compose(array $subCommand, ?Context $c = null, bool $withBuilder = false): Process +{ + $c ??= context(); + + $domains = [variable('root_domain'), ...variable('extra_domains')]; + $domains = '`' . implode('`) || Host(`', $domains) . '`'; + + $c = $c + ->withTimeout(null) + ->withEnvironment([ + 'PROJECT_NAME' => variable('project_name'), + 'PROJECT_ROOT_DOMAIN' => variable('root_domain'), + 'PROJECT_DOMAINS' => $domains, + 'USER_ID' => variable('user_id'), + 'COMPOSER_CACHE_DIR' => variable('composer_cache_dir'), + 'PHP_VERSION' => variable('php_version'), + 'BUILDKIT_PROGRESS' => 'plain', + ]) + ; + + $command = [ + 'docker', + 'compose', + '-p', variable('project_name'), + ]; + + foreach (variable('docker_compose_files') as $file) { + $command[] = '-f'; + $command[] = variable('root_dir') . '/infrastructure/docker/' . $file; + } + + if ($withBuilder) { + $command[] = '-f'; + $command[] = variable('root_dir') . '/infrastructure/docker/docker-compose.builder.yml'; + } + + $command = array_merge($command, $subCommand); + + return run($command, context: $c); +} + +function docker_compose_run( + string $runCommand, + ?Context $c = null, + string $service = 'builder', + bool $noDeps = true, + ?string $workDir = null, + bool $portMapping = false, + bool $withBuilder = true, +): Process { + $command = [ + 'run', + '--rm', + ]; + + if ($noDeps) { + $command[] = '--no-deps'; + } + + if ($portMapping) { + $command[] = '--service-ports'; + } + + if (null !== $workDir) { + $command[] = '-w'; + $command[] = $workDir; + } + + $command[] = $service; + $command[] = '/bin/sh'; + $command[] = '-c'; + $command[] = "exec {$runCommand}"; + + return docker_compose($command, c: $c, withBuilder: $withBuilder); +} + +function docker_exit_code( + string $runCommand, + ?Context $c = null, + string $service = 'builder', + bool $noDeps = true, + ?string $workDir = null, + bool $withBuilder = true, +): int { + $c = ($c ?? context())->withAllowFailure(); + + $process = docker_compose_run( + runCommand: $runCommand, + c: $c, + service: $service, + noDeps: $noDeps, + workDir: $workDir, + withBuilder: $withBuilder, + ); + + return $process->getExitCode() ?? 0; +} + +// Mac users have a lot of problems running Yarn / Webpack on the Docker stack +// so this func allow them to run these tools on their host +function run_in_docker_or_locally_for_mac(string $command, ?Context $c = null): void +{ + $c ??= context(); + + if (variable('macos')) { + run($command, context: $c->withPath(variable('root_dir'))); + } else { + docker_compose_run($command, c: $c); + } +} + +/** + * Find worker containers for the current project. + * + * @return array + */ +function get_workers(): array +{ + $command = [ + 'docker', + 'ps', + '-a', + '--filter', 'label=docker-starter.worker.' . variable('project_name'), + '--quiet', + ]; + $out = capture($command); + + if (!$out) { + return []; + } + + $workers = explode("\n", $out); + + return array_map('trim', $workers); +} diff --git a/examples/.castor/qa.php b/examples/.castor/qa.php new file mode 100644 index 0000000..32d5202 --- /dev/null +++ b/examples/.castor/qa.php @@ -0,0 +1,51 @@ +title('Installing QA tooling'); + + docker_compose_run('composer install -o', workDir: '/var/www/tools/php-cs-fixer'); + docker_compose_run('composer install -o', workDir: '/var/www/tools/phpstan'); +} + +// #[AsTask(description: 'Runs PHPUnit', aliases: ['phpunit'])] +// function phpunit(): int +// { +// return docker_exit_code('phpunit'); +// } + +#[AsTask(description: 'Runs PHPStan', aliases: ['phpstan'])] +function phpstan(): int +{ + return docker_exit_code('phpstan', workDir: '/var/www'); +} + +#[AsTask(description: 'Fixes Coding Style', aliases: ['cs'])] +function cs(bool $dryRun = false): int +{ + if ($dryRun) { + return docker_exit_code('php-cs-fixer fix --dry-run --diff', workDir: '/var/www'); + } + + return docker_exit_code('php-cs-fixer fix', workDir: '/var/www'); +} diff --git a/examples/.env b/examples/.env new file mode 100644 index 0000000..1bb8dab --- /dev/null +++ b/examples/.env @@ -0,0 +1,41 @@ +# In all environments, the following files are loaded if they exist, +# the latter taking precedence over the former: +# +# * .env contains default values for the environment variables needed by the app +# * .env.local uncommitted file with local overrides +# * .env.$APP_ENV committed environment-specific defaults +# * .env.$APP_ENV.local uncommitted environment-specific overrides +# +# Real environment variables win over .env files. +# +# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. +# https://symfony.com/doc/current/configuration/secrets.html +# +# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). +# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration + +###> symfony/framework-bundle ### +APP_ENV=dev +APP_SECRET=acbff843d79416cbb1432679dc4f3ead +###< symfony/framework-bundle ### + +###> doctrine/doctrine-bundle ### +# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml +# +# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" +# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" +# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" +DATABASE_URL="postgresql://app:app@postgres:5432/app?serverVersion=16&charset=utf8" +###< doctrine/doctrine-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +###< symfony/messenger ### + +###> symfony/mailer ### +# MAILER_DSN=null://null +###< symfony/mailer ### diff --git a/examples/.env.test b/examples/.env.test new file mode 100644 index 0000000..9e7162f --- /dev/null +++ b/examples/.env.test @@ -0,0 +1,6 @@ +# define your env variables for the test env here +KERNEL_CLASS='App\Kernel' +APP_SECRET='$ecretf0rt3st' +SYMFONY_DEPRECATIONS_HELPER=999999 +PANTHER_APP_ENV=panther +PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots diff --git a/examples/.gitattributes b/examples/.gitattributes new file mode 100644 index 0000000..1823cf6 --- /dev/null +++ b/examples/.gitattributes @@ -0,0 +1,2 @@ +# Force LF line ending (mandatory for Windows) +* text=auto eol=lf diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..2ea9c84 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,30 @@ +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/public/bundles/ +/var/ +/vendor/ +###< symfony/framework-bundle ### + +# Infra & tooling +/.castor.stub.php +/infrastructure/docker/docker-compose.override.yml +/infrastructure/docker/services/router/certs/*.pem +.php-cs-fixer.cache + +###> phpunit/phpunit ### +/phpunit.xml +.phpunit.result.cache +###< phpunit/phpunit ### + +###> symfony/phpunit-bridge ### +.phpunit.result.cache +/phpunit.xml +###< symfony/phpunit-bridge ### + +###> symfony/asset-mapper ### +/public/assets/ +/assets/vendor/ +###< symfony/asset-mapper ### diff --git a/examples/.php-cs-fixer.php b/examples/.php-cs-fixer.php new file mode 100644 index 0000000..29a627d --- /dev/null +++ b/examples/.php-cs-fixer.php @@ -0,0 +1,27 @@ +ignoreVCSIgnored(true) + ->ignoreDotFiles(false) + ->in(__DIR__) + ->append([ + __FILE__, + ]) +; + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PHP81Migration' => true, + '@PhpCsFixer' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + 'php_unit_internal_class' => false, // From @PhpCsFixer but we don't want it + 'php_unit_test_class_requires_covers' => false, // From @PhpCsFixer but we don't want it + 'phpdoc_add_missing_param_annotation' => false, // From @PhpCsFixer but we don't want it + 'concat_space' => ['spacing' => 'one'], + 'ordered_class_elements' => true, // Symfony(PSR12) override the default value, but we don't want + 'blank_line_before_statement' => true, // Symfony(PSR12) override the default value, but we don't want + ]) + ->setFinder($finder) +; diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..64af536 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,102 @@ +# My project + +## Running the application locally + +### Requirements + +A Docker environment is provided and requires you to have these tools available: + + * Docker + * Bash + * PHP >= 8.1 + * [Castor](https://github.com/jolicode/castor#installation) + +#### Castor + +Once castor is installed, in order to improve your usage of castor scripts, you +can install console autocompletion script. + +If you are using bash: + +```bash +castor completion | sudo tee /etc/bash_completion.d/castor +``` + +If you are using something else, please refer to your shell documentation. You +may need to use `castor completion > /to/somewhere`. + +Castor supports completion for `bash`, `zsh` & `fish` shells. + +### Docker environment + +The Docker infrastructure provides a web stack with: + - NGINX + - PostgreSQL + - PHP + - Traefik + - A container with some tooling: + - Composer + - Node + - Yarn / NPM + +### Domain configuration (first time only) + +Before running the application for the first time, ensure your domain names +point the IP of your Docker daemon by editing your `/etc/hosts` file. + +This IP is probably `127.0.0.1` unless you run Docker in a special VM (like docker-machine for example). + +> [!NOTE] +> The router binds port 80 and 443, that's why it will work with `127.0.0.1` + +``` +echo '127.0.0.1 app.test www.app.test' | sudo tee -a /etc/hosts +``` + +### Starting the stack + +Launch the stack by running this command: + +```bash +castor start +``` + +> [!NOTE] +> the first start of the stack should take a few minutes. + +The site is now accessible at the hostnames your have configured over HTTPS +(you may need to accept self-signed SSL certificate if you do not have mkcert +installed on your computer - see below). + +### SSL certificates + +HTTPS is supported out of the box. SSL certificates are not versioned and will +be generated the first time you start the infrastructure (`castor start`) or if +you run `castor infra:generate-certificates`. + +If you have `mkcert` installed on your computer, it will be used to generate +locally trusted certificates. See [`mkcert` documentation](https://github.com/FiloSottile/mkcert#installation) +to understand how to install it. Do not forget to install CA root from mkcert +by running `mkcert -install`. + +If you don't have `mkcert`, then self-signed certificates will instead be +generated with openssl. You can configure [infrastructure/docker/services/router/openssl.cnf](infrastructure/docker/services/router/openssl.cnf) +to tweak certificates. + +You can run `castor infra:generate-certificates --force` to recreate new certificates +if some were already generated. Remember to restart the infrastructure to make +use of the new certificates with `castor up` or `castor start`. + +### Builder + +Having some composer, yarn or other modifications to make on the project? +Start the builder which will give you access to a container with all these +tools available: + +```bash +castor builder +``` + +### Other tasks + +Checkout `castor` to have the list of available tasks. diff --git a/examples/assets/app.js b/examples/assets/app.js new file mode 100644 index 0000000..8725cc5 --- /dev/null +++ b/examples/assets/app.js @@ -0,0 +1,10 @@ +import './bootstrap.js'; +/* + * Welcome to your app's main JavaScript file! + * + * This file will be included onto the page via the importmap() Twig function, + * which should already be in your base.html.twig. + */ +import './styles/app.css'; + +console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉'); diff --git a/examples/assets/bootstrap.js b/examples/assets/bootstrap.js new file mode 100644 index 0000000..d4e50c9 --- /dev/null +++ b/examples/assets/bootstrap.js @@ -0,0 +1,5 @@ +import { startStimulusApp } from '@symfony/stimulus-bundle'; + +const app = startStimulusApp(); +// register any custom, 3rd party controllers here +// app.register('some_controller_name', SomeImportedController); diff --git a/examples/assets/controllers.json b/examples/assets/controllers.json new file mode 100644 index 0000000..29ea244 --- /dev/null +++ b/examples/assets/controllers.json @@ -0,0 +1,15 @@ +{ + "controllers": { + "@symfony/ux-turbo": { + "turbo-core": { + "enabled": true, + "fetch": "eager" + }, + "mercure-turbo-stream": { + "enabled": false, + "fetch": "eager" + } + } + }, + "entrypoints": [] +} diff --git a/examples/assets/controllers/hello_controller.js b/examples/assets/controllers/hello_controller.js new file mode 100644 index 0000000..e847027 --- /dev/null +++ b/examples/assets/controllers/hello_controller.js @@ -0,0 +1,16 @@ +import { Controller } from '@hotwired/stimulus'; + +/* + * This is an example Stimulus controller! + * + * Any element with a data-controller="hello" attribute will cause + * this controller to be executed. The name "hello" comes from the filename: + * hello_controller.js -> "hello" + * + * Delete this file or adapt it for your use! + */ +export default class extends Controller { + connect() { + this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js'; + } +} diff --git a/examples/assets/styles/app.css b/examples/assets/styles/app.css new file mode 100644 index 0000000..dd6181a --- /dev/null +++ b/examples/assets/styles/app.css @@ -0,0 +1,3 @@ +body { + background-color: skyblue; +} diff --git a/examples/bin/console b/examples/bin/console new file mode 100755 index 0000000..c933dc5 --- /dev/null +++ b/examples/bin/console @@ -0,0 +1,17 @@ +#!/usr/bin/env php += 80000) { + require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit'; + } else { + define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); + require PHPUNIT_COMPOSER_INSTALL; + PHPUnit\TextUI\Command::main(); + } +} else { + if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { + echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; + exit(1); + } + + require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php'; +} diff --git a/examples/castor.php b/examples/castor.php new file mode 100644 index 0000000..a07f644 --- /dev/null +++ b/examples/castor.php @@ -0,0 +1,81 @@ + + */ +function create_default_variables(): array +{ + return [ + 'project_name' => 'symfony-metrics', + 'root_domain' => 'symfony-metrics.test', + 'extra_domains' => [ + 'grafana.symfony-metrics.test', + ], + ]; +} + +#[AsTask(description: 'Builds and starts the infrastructure, then install the application (composer, yarn, ...)')] +function start(): void +{ + io()->title('Starting the stack'); + + workers_stop(); + generate_certificates(force: false); + build(); + up(); + cache_clear(); + install(); + migrate(); + workers_start(); + + notify('The stack is now up and running.'); + io()->success('The stack is now up and running.'); + + about(); +} + +#[AsTask(description: 'Installs the application (composer, yarn, ...)', namespace: 'app', aliases: ['install'])] +function install(): void +{ + io()->title('Installing the application'); + + io()->section('Installing PHP dependencies'); + docker_compose_run('composer install -n --prefer-dist --optimize-autoloader'); + + io()->section('Installing importmap'); + docker_compose_run('bin/console importmap:install'); + + qa\install(); +} + +#[AsTask(description: 'Clear the application cache', namespace: 'app', aliases: ['cache-clear'])] +function cache_clear(): void +{ + io()->title('Clearing the application cache'); + + docker_compose_run('rm -rf var/cache/ && bin/console cache:warmup'); +} + +#[AsTask(description: 'Migrates database schema', namespace: 'app:db', aliases: ['migrate'])] +function migrate(): void +{ + io()->title('Migrating the database schema'); + + docker_compose_run('bin/console doctrine:database:create --if-not-exists'); + docker_compose_run('bin/console doctrine:migration:migrate -n --allow-no-migration'); +} diff --git a/examples/composer.json b/examples/composer.json new file mode 100644 index 0000000..8d11ee5 --- /dev/null +++ b/examples/composer.json @@ -0,0 +1,108 @@ +{ + "type": "project", + "license": "proprietary", + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "php": ">=8.2", + "ext-ctype": "*", + "ext-iconv": "*", + "doctrine/dbal": "^3", + "doctrine/doctrine-bundle": "^2.11", + "doctrine/doctrine-migrations-bundle": "^3.3", + "doctrine/orm": "^3.0", + "phpdocumentor/reflection-docblock": "^5.3", + "phpstan/phpdoc-parser": "^1.26", + "symfony/asset": "7.0.*", + "symfony/asset-mapper": "7.0.*", + "symfony/console": "7.0.*", + "symfony/doctrine-messenger": "7.0.*", + "symfony/dotenv": "7.0.*", + "symfony/expression-language": "7.0.*", + "symfony/flex": "^2", + "symfony/form": "7.0.*", + "symfony/framework-bundle": "7.0.*", + "symfony/http-client": "7.0.*", + "symfony/intl": "7.0.*", + "symfony/mailer": "7.0.*", + "symfony/mime": "7.0.*", + "symfony/monolog-bundle": "^3.0", + "symfony/notifier": "7.0.*", + "symfony/process": "7.0.*", + "symfony/property-access": "7.0.*", + "symfony/property-info": "7.0.*", + "symfony/runtime": "7.0.*", + "symfony/security-bundle": "7.0.*", + "symfony/serializer": "7.0.*", + "symfony/stimulus-bundle": "^2.16", + "symfony/string": "7.0.*", + "symfony/translation": "7.0.*", + "symfony/twig-bundle": "7.0.*", + "symfony/ux-turbo": "^2.16", + "symfony/validator": "7.0.*", + "symfony/web-link": "7.0.*", + "symfony/yaml": "7.0.*", + "twig/extra-bundle": "^2.12|^3.0", + "twig/twig": "^2.12|^3.0" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests/" + } + }, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*", + "symfony/polyfill-php82": "*" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd", + "importmap:install": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "7.0.*" + } + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/browser-kit": "7.0.*", + "symfony/css-selector": "7.0.*", + "symfony/debug-bundle": "7.0.*", + "symfony/maker-bundle": "^1.0", + "symfony/phpunit-bridge": "^7.0", + "symfony/stopwatch": "7.0.*", + "symfony/web-profiler-bundle": "7.0.*" + } +} diff --git a/examples/composer.lock b/examples/composer.lock new file mode 100644 index 0000000..7e98a61 --- /dev/null +++ b/examples/composer.lock @@ -0,0 +1,9702 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a081757e3f87e400b4b604ce219bee18", + "packages": [ + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "doctrine/cache", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "shasum": "" + }, + "require": { + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", + "keywords": [ + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" + ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" + }, + { + "name": "doctrine/collections", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "07e16cd7b80a2cffed99e36b541876af172f0257" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/07e16cd7b80a2cffed99e36b541876af172f0257", + "reference": "07e16cd7b80a2cffed99e36b541876af172f0257", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1", + "php": "^8.1" + }, + "require-dev": { + "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\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", + "keywords": [ + "array", + "collections", + "iterators", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" + } + ], + "time": "2024-02-25T22:55:36+00:00" + }, + { + "name": "doctrine/dbal", + "version": "3.8.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", + "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.57", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "9.6.16", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.8.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." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.8.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2024-02-12T18:36:36+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "2.11.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/492725310ae9a1b5b20d6ae09fb5ae6404616e68", + "reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/persistence": "^2.2 || ^3", + "doctrine/sql-formatter": "^1.0.1", + "php": "^7.4 || ^8.0", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/doctrine-bridge": "^5.4.19 || ^6.0.7 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3" + }, + "conflict": { + "doctrine/annotations": ">=3.0", + "doctrine/orm": "<2.17 || >=4.0", + "twig/twig": "<1.34 || >=2.0 <2.4" + }, + "require-dev": { + "doctrine/annotations": "^1 || ^2", + "doctrine/coding-standard": "^12", + "doctrine/deprecations": "^1.0", + "doctrine/orm": "^2.17 || ^3.0", + "friendsofphp/proxy-manager-lts": "^1.0", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-symfony": "^5", + "psr/log": "^1.1.4 || ^2.0 || ^3.0", + "symfony/phpunit-bridge": "^6.1 || ^7.0", + "symfony/property-info": "^5.4 || ^6.0 || ^7.0", + "symfony/proxy-manager-bridge": "^5.4 || ^6.0 || ^7.0", + "symfony/security-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/string": "^5.4 || ^6.0 || ^7.0", + "symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0", + "symfony/validator": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^5.4 || ^6.2 || ^7.0", + "symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0", + "twig/twig": "^1.34 || ^2.12 || ^3.0", + "vimeo/psalm": "^5.15" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "ext-pdo": "*", + "symfony/web-profiler-bundle": "To use the data collector." + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org/" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineBundle/issues", + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.11.3" + }, + "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%2Fdoctrine-bundle", + "type": "tidelift" + } + ], + "time": "2024-02-10T20:56:20+00:00" + }, + { + "name": "doctrine/doctrine-migrations-bundle", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1dd42906a5fb9c5960723e2ebb45c68006493835", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.4", + "doctrine/migrations": "^3.2", + "php": "^7.2|^8.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.6 || ^3", + "doctrine/persistence": "^2.0 || ^3 ", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^8.5|^9.5", + "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-symfony": "^3 || ^5", + "symfony/phpunit-bridge": "^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6 || ^7", + "vimeo/psalm": "^4.30 || ^5.15" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\MigrationsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineMigrationsBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "dbal", + "migrations", + "schema" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-migrations-bundle", + "type": "tidelift" + } + ], + "time": "2023-11-13T19:44:41+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2022-10-12T20:59:15+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "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": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "doctrine/migrations", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/47af29eef49f29ebee545947e8b2a4b3be318c8a", + "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/dbal": "^3.5.1 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2.0", + "php": "^8.1", + "psr/log": "^1.1.3 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0" + }, + "conflict": { + "doctrine/orm": "<2.12 || >=4" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3", + "doctrine/sql-formatter": "^1.0", + "ext-pdo_sqlite": "*", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^10.3", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", + "symfony/yaml": "Allows the use of yaml for migration configuration files." + }, + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", + "homepage": "https://www.doctrine-project.org/projects/migrations.html", + "keywords": [ + "database", + "dbal", + "migrations" + ], + "support": { + "issues": "https://github.com/doctrine/migrations/issues", + "source": "https://github.com/doctrine/migrations/tree/3.7.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", + "type": "tidelift" + } + ], + "time": "2023-12-05T11:35:05+00:00" + }, + { + "name": "doctrine/orm", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/orm.git", + "reference": "2a250b5814de192a23438c0a43e15da7e77890a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/orm/zipball/2a250b5814de192a23438c0a43e15da7e77890a7", + "reference": "2a250b5814de192a23438c0a43e15da7e77890a7", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/collections": "^2.1", + "doctrine/dbal": "^3.8.2 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2", + "doctrine/inflector": "^1.4 || ^2.0", + "doctrine/instantiator": "^1.3 || ^2", + "doctrine/lexer": "^3", + "doctrine/persistence": "^3.1.1", + "ext-ctype": "*", + "php": "^8.1", + "psr/cache": "^1 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "~6.2.13 || ^6.3.2 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0", + "phpbench/phpbench": "^1.0", + "phpstan/phpstan": "1.10.35", + "phpunit/phpunit": "^10.4.0", + "psr/log": "^1 || ^2 || ^3", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^5.4 || ^6.2 || ^7.0", + "vimeo/psalm": "5.16.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" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\ORM\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "https://www.doctrine-project.org/projects/orm.html", + "keywords": [ + "database", + "orm" + ], + "support": { + "issues": "https://github.com/doctrine/orm/issues", + "source": "https://github.com/doctrine/orm/tree/3.0.1" + }, + "time": "2024-02-22T12:23:53+00:00" + }, + { + "name": "doctrine/persistence", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/persistence.git", + "reference": "63fee8c33bef740db6730eb2a750cd3da6495603" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603", + "reference": "63fee8c33bef740db6730eb2a750cd3da6495603", + "shasum": "" + }, + "require": { + "doctrine/event-manager": "^1 || ^2", + "php": "^7.2 || ^8.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0" + }, + "conflict": { + "doctrine/common": "<2.10" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "doctrine/coding-standard": "^11", + "doctrine/common": "^3.0", + "phpstan/phpstan": "1.9.4", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6.0", + "vimeo/psalm": "4.30.0 || 5.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Persistence\\": "src/Persistence" + } + }, + "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" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", + "homepage": "https://www.doctrine-project.org/projects/persistence.html", + "keywords": [ + "mapper", + "object", + "odm", + "orm", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/persistence/issues", + "source": "https://github.com/doctrine/persistence/tree/3.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence", + "type": "tidelift" + } + ], + "time": "2023-05-17T18:32:04+00:00" + }, + { + "name": "doctrine/sql-formatter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/sql-formatter.git", + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/a321d114e0a18e6497f8a2cd6f890e000cc17ecc", + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4" + }, + "bin": [ + "bin/sql-formatter" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\SqlFormatter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "https://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/doctrine/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "support": { + "issues": "https://github.com/doctrine/sql-formatter/issues", + "source": "https://github.com/doctrine/sql-formatter/tree/1.2.0" + }, + "time": "2023-08-16T21:49:04+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.1", + "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": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2023-10-27T15:32:31+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@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" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "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": "phpstan/phpdoc-parser", + "version": "1.26.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "231e3186624c03d7e7c890ec662b81e6b0405227" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227", + "reference": "231e3186624c03d7e7c890ec662b81e6b0405227", + "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/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0" + }, + "time": "2024-02-23T16:05:55+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/link", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/link.git", + "reference": "84b159194ecfd7eaa472280213976e96415433f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/link/zipball/84b159194ecfd7eaa472280213976e96415433f7", + "reference": "84b159194ecfd7eaa472280213976e96415433f7", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "suggest": { + "fig/link-util": "Provides some useful PSR-13 utilities" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Link\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for HTTP links", + "homepage": "https://github.com/php-fig/link", + "keywords": [ + "http", + "http-link", + "link", + "psr", + "psr-13", + "rest" + ], + "support": { + "source": "https://github.com/php-fig/link/tree/2.0.1" + }, + "time": "2021-03-11T23:00:27+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "symfony/asset", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "3ae493792fc17cc31b84e231f30f2d154575f171" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/3ae493792fc17cc31b84e231f30f2d154575f171", + "reference": "3ae493792fc17cc31b84e231f30f2d154575f171", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "symfony/http-foundation": "<6.4" + }, + "require-dev": { + "symfony/http-client": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/asset/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/asset-mapper", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset-mapper.git", + "reference": "7cd421551f53849f641a1a3499392ed5c81598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/7cd421551f53849f641a1a3499392ed5c81598a8", + "reference": "7cd421551f53849f641a1a3499392ed5c81598a8", + "shasum": "" + }, + "require": { + "composer/semver": "^3.0", + "php": ">=8.2", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0" + }, + "conflict": { + "symfony/framework-bundle": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/event-dispatcher-contracts": "^3.0", + "symfony/finder": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\AssetMapper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/asset-mapper/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/cache", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/fc822951dd360a593224bb2cef90a087d0dff60f", + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1d74b127da04ffa87aa940abe15446fa89653778", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.4.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": "2023-09-25T12:52:38+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "1c680e565dc0044d8ed3baeb57835fcacd9c6aed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/1c680e565dc0044d8ed3baeb57835fcacd9c6aed", + "reference": "1c680e565dc0044d8ed3baeb57835fcacd9c6aed", + "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.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/config", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "44deeba7233f08f383185ffa37dace3b3bc87364" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/44deeba7233f08f383185ffa37dace3b3bc87364", + "reference": "44deeba7233f08f383185ffa37dace3b3bc87364", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^6.4|^7.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-26T07:52:39+00:00" + }, + { + "name": "symfony/console", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "47f37af245df8457ea63409fc242b3cc825ce5eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/47f37af245df8457ea63409fc242b3cc825ce5eb", + "reference": "47f37af245df8457ea63409fc242b3cc825ce5eb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.3", + "symfony/var-exporter": "^6.4|^7.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "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.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/doctrine-bridge", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/doctrine-bridge.git", + "reference": "aded7ef586f9c75a04627326a5748f29ceba3506" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/aded7ef586f9c75a04627326a5748f29ceba3506", + "reference": "aded7ef586f9c75a04627326a5748f29ceba3506", + "shasum": "" + }, + "require": { + "doctrine/event-manager": "^2", + "doctrine/persistence": "^3.1", + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "doctrine/lexer": "<1.1", + "doctrine/orm": "<2.15", + "symfony/cache": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/form": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/lock": "<6.4", + "symfony/messenger": "<6.4", + "symfony/property-info": "<6.4", + "symfony/security-bundle": "<6.4", + "symfony/security-core": "<6.4", + "symfony/validator": "<6.4" + }, + "require-dev": { + "doctrine/collections": "^1.0|^2.0", + "doctrine/data-fixtures": "^1.1", + "doctrine/dbal": "^3.6|^4", + "doctrine/orm": "^2.15|^3", + "psr/log": "^1|^2|^3", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/doctrine-messenger": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Doctrine with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/doctrine-bridge/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-04T16:21:40+00:00" + }, + { + "name": "symfony/doctrine-messenger", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/doctrine-messenger.git", + "reference": "5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca", + "reference": "5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^3.6|^4", + "php": ">=8.2", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/persistence": "<1.3" + }, + "require-dev": { + "doctrine/persistence": "^1.3|^2|^3", + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "type": "symfony-messenger-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Doctrine Messenger Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/doctrine-messenger/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "symfony/console": "<6.4", + "symfony/process": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-09T10:53:15+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "677b24759decff69e65b1e9d1471d90f95ced880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/677b24759decff69e65b1e9d1471d90f95ced880", + "reference": "677b24759decff69e65b1e9d1471d90f95ced880", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e", + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/expression-language", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "0877c599cb260c9614f9229c0a2090d6919fd621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/0877c599cb260c9614f9229c0a2090d6919fd621", + "reference": "0877c599cb260c9614f9229c0a2090d6919fd621", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/cache": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an engine that can compile and evaluate expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/expression-language/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.0.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": "2023-10-31T17:59:56+00:00" + }, + { + "name": "symfony/flex", + "version": "v2.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/flex.git", + "reference": "bec213c39511eda66663baa2ee7440c65f89c695" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/flex/zipball/bec213c39511eda66663baa2ee7440c65f89c695", + "reference": "bec213c39511eda66663baa2ee7440c65f89c695", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.1", + "php": ">=8.0" + }, + "require-dev": { + "composer/composer": "^2.1", + "symfony/dotenv": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Symfony\\Flex\\Flex" + }, + "autoload": { + "psr-4": { + "Symfony\\Flex\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "Composer plugin for Symfony", + "support": { + "issues": "https://github.com/symfony/flex/issues", + "source": "https://github.com/symfony/flex/tree/v2.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-05T18:04:53+00:00" + }, + { + "name": "symfony/form", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/form.git", + "reference": "5cfe85c74caf924c7cec2134e169320b464ede84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/form/zipball/5cfe85c74caf924c7cec2134e169320b464ede84", + "reference": "5cfe85c74caf924c7cec2134e169320b464ede84", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/options-resolver": "^6.4|^7.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/error-handler": "<6.4", + "symfony/framework-bundle": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "doctrine/collections": "^1.0|^2.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/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/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/translation": "^6.4.3|^7.0.3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Form\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows to easily create, process and reuse HTML forms", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/form/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-12T11:15:03+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21", + "reference": "b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^6.4|^7.0" + }, + "conflict": { + "doctrine/persistence": "<1.3", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/asset": "<6.4", + "symfony/asset-mapper": "<6.4", + "symfony/clock": "<6.4", + "symfony/console": "<6.4", + "symfony/dom-crawler": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/lock": "<6.4", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/property-access": "<6.4", + "symfony/property-info": "<6.4", + "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", + "symfony/security-core": "<6.4", + "symfony/security-csrf": "<6.4", + "symfony/serializer": "<6.4", + "symfony/stopwatch": "<6.4", + "symfony/translation": "<6.4", + "symfony/twig-bridge": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4", + "symfony/web-profiler-bundle": "<6.4", + "symfony/workflow": "<6.4" + }, + "require-dev": { + "doctrine/persistence": "^1.3|^2|^3", + "dragonmantank/cron-expression": "^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "seld/jsonlint": "^1.10", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/dotenv": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/mailer": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/notifier": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/scheduler": "^6.4.4|^7.0.4", + "symfony/security-bundle": "^6.4|^7.0", + "symfony/semaphore": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/framework-bundle/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-26T07:52:39+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "8384876f49a2316a63f88a9cd12436de6936bee6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/8384876f49a2316a63f88a9cd12436de6936bee6", + "reference": "8384876f49a2316a63f88a9cd12436de6936bee6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/http-client-contracts": "^3", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "1ee70e699b41909c209a0c930f11034b93578654" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", + "reference": "1ee70e699b41909c209a0c930f11034b93578654", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.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": "2023-07-30T20:28:31+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "439fdfdd344943254b1ef6278613e79040548045" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/439fdfdd344943254b1ef6278613e79040548045", + "reference": "439fdfdd344943254b1ef6278613e79040548045", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4" + }, + "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": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-08T19:22:56+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/065e2234d907c0fc4fc942bf223f7cfc016d0ac7", + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.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": "^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": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "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-exporter": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-27T06:35:35+00:00" + }, + { + "name": "symfony/intl", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "295995df4acf6790a35b9ce6ec32b313efb11ff8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/295995df4acf6790a35b9ce6ec32b313efb11ff8", + "reference": "295995df4acf6790a35b9ce6ec32b313efb11ff8", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides access to the localization data of the ICU library", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "support": { + "source": "https://github.com/symfony/intl/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "72e16d87bf50a3ce195b9470c06bb9d7b816ea85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/72e16d87bf50a3ce195b9470c06bb9d7b816ea85", + "reference": "72e16d87bf50a3ce195b9470c06bb9d7b816ea85", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-03T21:34:19+00:00" + }, + { + "name": "symfony/messenger", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/messenger.git", + "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/messenger/zipball/804a8997f93313a8f7ed19e8cca3b44fdd18bdec", + "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/clock": "^6.4|^7.0" + }, + "conflict": { + "symfony/console": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/framework-bundle": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/serializer": "<6.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\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/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-26T07:52:39+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716", + "reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-30T08:34:29+00:00" + }, + { + "name": "symfony/monolog-bridge", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", + "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", + "shasum": "" + }, + "require": { + "monolog/monolog": "^3", + "php": ">=8.2", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/security-core": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/mailer": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Monolog\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Monolog with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/monolog-bridge/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/monolog-bundle", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "shasum": "" + }, + "require": { + "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", + "php": ">=7.2.5", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony MonologBundle", + "homepage": "https://symfony.com", + "keywords": [ + "log", + "logging" + ], + "support": { + "issues": "https://github.com/symfony/monolog-bundle/issues", + "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.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": "2023-11-06T17:08:13+00:00" + }, + { + "name": "symfony/notifier", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/notifier.git", + "reference": "515326dab227ecd3198b04df50edf949f7767a9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/notifier/zipball/515326dab227ecd3198b04df50edf949f7767a9a", + "reference": "515326dab227ecd3198b04df50edf949f7767a9a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3" + }, + "conflict": { + "symfony/event-dispatcher": "<6.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Notifier\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Sends notifications via one or more channels (email, SMS, ...)", + "homepage": "https://symfony.com", + "keywords": [ + "notification", + "notifier" + ], + "support": { + "source": "https://github.com/symfony/notifier/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "700ff4096e346f54cb628ea650767c8130f1001f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", + "reference": "700ff4096e346f54cb628ea650767c8130f1001f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.0.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": "2023-08-08T10:20:21+00:00" + }, + { + "name": "symfony/password-hasher", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/password-hasher.git", + "reference": "0eba656c16ecdf5588b3ddd2b2337b06173d839f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/0eba656c16ecdf5588b3ddd2b2337b06173d839f", + "reference": "0eba656c16ecdf5588b3ddd2b2337b06173d839f", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "symfony/security-core": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PasswordHasher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Chalas", + "email": "robin.chalas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides password hashing utilities", + "homepage": "https://symfony.com", + "keywords": [ + "hashing", + "password" + ], + "support": { + "source": "https://github.com/symfony/password-hasher/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-12T11:15:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/07094a28851a49107f3ab4f9120ca2975a64b6e1", + "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance and support of other locales than \"en\"" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Icu\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "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": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.29.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-01-29T20:12:16+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/property-access", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "44e3746d4de8d0961a44ee332c74dd0918266127" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/44e3746d4de8d0961a44ee332c74dd0918266127", + "reference": "44e3746d4de8d0961a44ee332c74dd0918266127", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/property-info": "^6.4|^7.0" + }, + "require-dev": { + "symfony/cache": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides functions to read and write from/to an object or array using a simple string notation", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property-path", + "reflection" + ], + "support": { + "source": "https://github.com/symfony/property-access/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-16T13:44:10+00:00" + }, + { + "name": "symfony/property-info", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-info.git", + "reference": "e160f92ea827243abf2dbf36b8460b1377194406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-info/zipball/e160f92ea827243abf2dbf36b8460b1377194406", + "reference": "e160f92ea827243abf2dbf36b8460b1377194406", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/dependency-injection": "<6.4", + "symfony/serializer": "<6.4" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^5.2", + "phpstan/phpdoc-parser": "^1.0", + "symfony/cache": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts information about PHP class' properties using metadata of popular sources", + "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], + "support": { + "source": "https://github.com/symfony/property-info/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/858b26756ffc35a11238b269b484ee3a393a74d3", + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-30T13:55:15+00:00" + }, + { + "name": "symfony/runtime", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/runtime.git", + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/runtime/zipball/ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": ">=8.2" + }, + "conflict": { + "symfony/dotenv": "<6.4" + }, + "require-dev": { + "composer/composer": "^2.6", + "symfony/console": "^6.4|^7.0", + "symfony/dotenv": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Runtime\\": "", + "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Enables decoupling PHP applications from global state", + "homepage": "https://symfony.com", + "keywords": [ + "runtime" + ], + "support": { + "source": "https://github.com/symfony/runtime/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/security-bundle", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-bundle.git", + "reference": "1755fb50e4da0a0013569752be763d208d690bcd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/1755fb50e4da0a0013569752be763d208d690bcd", + "reference": "1755fb50e4da0a0013569752be763d208d690bcd", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/clock": "^6.4|^7.0", + "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/password-hasher": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/console": "<6.4", + "symfony/framework-bundle": "<6.4", + "symfony/http-client": "<6.4", + "symfony/ldap": "<6.4", + "symfony/serializer": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/browser-kit": "^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/expression-language": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/ldap": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0.4", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1", + "web-token/jwt-signature-algorithm-eddsa": "^3.1", + "web-token/jwt-signature-algorithm-hmac": "^3.1", + "web-token/jwt-signature-algorithm-none": "^3.1", + "web-token/jwt-signature-algorithm-rsa": "^3.1" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SecurityBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-bundle/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/security-core", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-core.git", + "reference": "72b9d961a5dcd21e6bc29b99df51a9000a15dde0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-core/zipball/72b9d961a5dcd21e6bc29b99df51a9000a15dde0", + "reference": "72b9d961a5dcd21e6bc29b99df51a9000a15dde0", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/password-hasher": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/event-dispatcher": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/ldap": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/validator": "<6.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "psr/container": "^1.1|^2.0", + "psr/log": "^1|^2|^3", + "symfony/cache": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/ldap": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4.3|^7.0.3", + "symfony/validator": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Core\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - Core Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-core/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/security-csrf", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-csrf.git", + "reference": "f0f724e599f069b768e335e4bdf795726c7dfe8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/f0f724e599f069b768e335e4bdf795726c7dfe8e", + "reference": "f0f724e599f069b768e335e4bdf795726c7dfe8e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/security-core": "^6.4|^7.0" + }, + "conflict": { + "symfony/http-foundation": "<6.4" + }, + "require-dev": { + "symfony/http-foundation": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Csrf\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - CSRF Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-csrf/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/security-http", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-http.git", + "reference": "f3a70a937128f47366821a9f4b5dbfaa0ba9c862" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-http/zipball/f3a70a937128f47366821a9f4b5dbfaa0ba9c862", + "reference": "f3a70a937128f47366821a9f4b5dbfaa0ba9c862", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/clock": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/http-client-contracts": "<3.0", + "symfony/security-bundle": "<6.4", + "symfony/security-csrf": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/cache": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-client-contracts": "^3.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Http\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - HTTP Integration", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-http/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-26T07:52:39+00:00" + }, + { + "name": "symfony/serializer", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", + "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<6.4", + "symfony/property-access": "<6.4", + "symfony/property-info": "<6.4", + "symfony/uid": "<6.4", + "symfony/validator": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "seld/jsonlint": "^1.10", + "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/error-handler": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^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-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", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-26T14:02:43+00:00" + }, + { + "name": "symfony/stimulus-bundle", + "version": "v2.16.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stimulus-bundle.git", + "reference": "6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634", + "reference": "6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^2.0|^3.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "twig/twig": "^2.15.3|~3.8.0" + }, + "require-dev": { + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "zenstruck/browser": "^1.4" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\UX\\StimulusBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Integration with your Symfony app & Stimulus!", + "keywords": [ + "symfony-ux" + ], + "support": { + "source": "https://github.com/symfony/stimulus-bundle/tree/v2.16.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-02-29T16:20:46+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/string", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-01T13:17:36+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/5b75e872f7d135d7abb4613809fadc8d9f3d30a0", + "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0", + "shasum": "" + }, + "require": { + "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\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "06450585bf65e978026bda220cdebca3f867fde7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", + "reference": "06450585bf65e978026bda220cdebca3f867fde7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-26T14:02:43+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<6.4", + "symfony/form": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.4", + "symfony/serializer": "<6.4", + "symfony/translation": "<6.4", + "symfony/workflow": "<6.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/twig-bundle", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "php": ">=8.2", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "conflict": { + "symfony/framework-bundle": "<6.4", + "symfony/translation": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bundle/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/ux-turbo", + "version": "v2.16.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-turbo.git", + "reference": "d3590a43fee73304855dfc8022ccb57b0df9f03d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/d3590a43fee73304855dfc8022ccb57b0df9f03d", + "reference": "d3590a43fee73304855dfc8022ccb57b0df9f03d", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/stimulus-bundle": "^2.9.1" + }, + "conflict": { + "symfony/flex": "<1.13" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^2.4.3", + "doctrine/orm": "^2.8 | 3.0", + "phpstan/phpstan": "^1.10", + "symfony/debug-bundle": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/mercure-bundle": "^0.3.7", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/panther": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|6.3.*|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/security-core": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/web-profiler-bundle": "^5.4|^6.0|^7.0", + "symfony/webpack-encore-bundle": "^2.1.1" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "name": "symfony/ux", + "url": "https://github.com/symfony/ux" + } + }, + "autoload": { + "psr-4": { + "Symfony\\UX\\Turbo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "kevin@dunglas.fr" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Hotwire Turbo integration for Symfony", + "homepage": "https://symfony.com", + "keywords": [ + "hotwire", + "javascript", + "mercure", + "symfony-ux", + "turbo", + "turbo-stream" + ], + "support": { + "source": "https://github.com/symfony/ux-turbo/tree/v2.16.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-02-20T16:11:17+00:00" + }, + { + "name": "symfony/validator", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "104bc3620d0ee4091034cfbcdcf82ed727f15b7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/104bc3620d0ee4091034cfbcdcf82ed727f15b7d", + "reference": "104bc3620d0ee4091034cfbcdcf82ed727f15b7d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<7.0", + "symfony/expression-language": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/intl": "<6.4", + "symfony/property-info": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "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/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to validate values", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/validator/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-26T10:35:24+00:00" + }, + { + "name": "symfony/web-link", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/web-link.git", + "reference": "855a347feb2ecfc1d1a379c739aff956d4cbec00" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/web-link/zipball/855a347feb2ecfc1d1a379c739aff956d4cbec00", + "reference": "855a347feb2ecfc1d1a379c739aff956d4cbec00", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/link": "^1.1|^2.0" + }, + "conflict": { + "symfony/http-kernel": "<6.4" + }, + "provide": { + "psr/link-implementation": "1.0|2.0" + }, + "require-dev": { + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\WebLink\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages links between resources", + "homepage": "https://symfony.com", + "keywords": [ + "dns-prefetch", + "http", + "http2", + "link", + "performance", + "prefetch", + "preload", + "prerender", + "psr13", + "push" + ], + "support": { + "source": "https://github.com/symfony/web-link/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "2d4fca631c00700597e9442a0b2451ce234513d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2d4fca631c00700597e9442a0b2451ce234513d3", + "reference": "2d4fca631c00700597e9442a0b2451ce234513d3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "twig/extra-bundle", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/twig-extra-bundle.git", + "reference": "32807183753de0388c8e59f7ac2d13bb47311140" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/32807183753de0388c8e59f7ac2d13bb47311140", + "reference": "32807183753de0388c8e59f7ac2d13bb47311140", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "league/commonmark": "^1.0|^2.0", + "symfony/phpunit-bridge": "^6.4|^7.0", + "twig/cache-extra": "^3.0", + "twig/cssinliner-extra": "^2.12|^3.0", + "twig/html-extra": "^2.12|^3.0", + "twig/inky-extra": "^2.12|^3.0", + "twig/intl-extra": "^2.12|^3.0", + "twig/markdown-extra": "^2.12|^3.0", + "twig/string-extra": "^2.12|^3.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Twig\\Extra\\TwigExtraBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Symfony bundle for extra Twig extensions", + "homepage": "https://twig.symfony.com", + "keywords": [ + "bundle", + "extra", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T14:02:01+00:00" + }, + { + "name": "twig/twig", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T18:54:41+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "masterminds/html5", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + }, + "time": "2023-05-10T11:58:31+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69", + "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1" + }, + "time": "2024-02-21T19:24:10+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.30", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.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 provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:47:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "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": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "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.17", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "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.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/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.17" + }, + "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-02-23T13:14:51+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "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" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "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" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "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" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "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" + } + ], + "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" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "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": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "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" + } + }, + "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": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "725d5b15681685ac17b20b575254c75639722488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/725d5b15681685ac17b20b575254c75639722488", + "reference": "725d5b15681685ac17b20b575254c75639722488", + "shasum": "" + }, + "require": { + "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\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ec60a4edf94e63b0556b6a0888548bb400a3a3be", + "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/debug-bundle", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug-bundle.git", + "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/b0db5c443883ce5c10c2265c77feb9833c3d9d6d", + "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": ">=8.2", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4" + }, + "require-dev": { + "symfony/config": "^6.4|^7.0", + "symfony/web-profiler-bundle": "^6.4|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\DebugBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug-bundle/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "6cb272cbec4dc7a30a853d2931766b03bea92dda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/6cb272cbec4dc7a30a853d2931766b03bea92dda", + "reference": "6cb272cbec4dc7a30a853d2931766b03bea92dda", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-12T11:15:03+00:00" + }, + { + "name": "symfony/maker-bundle", + "version": "v1.55.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/maker-bundle.git", + "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/11a9d3125c5b93ab4043f0f2e9927fdc55881c17", + "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "nikic/php-parser": "^4.18|^5.0", + "php": ">=8.1", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.2|^3", + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" + }, + "conflict": { + "doctrine/doctrine-bundle": "<2.10", + "doctrine/orm": "<2.15" + }, + "require-dev": { + "composer/semver": "^3.0", + "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/orm": "^2.15|^3", + "symfony/http-client": "^6.4|^7.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0|^4.x-dev" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MakerBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", + "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", + "keywords": [ + "code generator", + "dev", + "generator", + "scaffold", + "scaffolding" + ], + "support": { + "issues": "https://github.com/symfony/maker-bundle/issues", + "source": "https://github.com/symfony/maker-bundle/tree/v1.55.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-21T13:41:51+00:00" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "conflict": { + "phpunit/phpunit": "<7.5|9.1.2" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.4|^7.0", + "symfony/polyfill-php81": "^1.27" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-08T19:22:56+00:00" + }, + { + "name": "symfony/web-profiler-bundle", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/web-profiler-bundle.git", + "reference": "542daea1345fe181cbfd52db00717174a838ea0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/542daea1345fe181cbfd52db00717174a838ea0a", + "reference": "542daea1345fe181cbfd52db00717174a838ea0a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/config": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "conflict": { + "symfony/form": "<6.4", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4" + }, + "require-dev": { + "symfony/browser-kit": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\WebProfilerBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a development tool that gives detailed information about the execution of any request", + "homepage": "https://symfony.com", + "keywords": [ + "dev" + ], + "support": { + "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2023-11-20T00:12:19+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.2", + "ext-ctype": "*", + "ext-iconv": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/examples/config/bundles.php b/examples/config/bundles.php new file mode 100644 index 0000000..4e3a560 --- /dev/null +++ b/examples/config/bundles.php @@ -0,0 +1,16 @@ + ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], + Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true], + Symfony\UX\Turbo\TurboBundle::class => ['all' => true], + Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], + Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], +]; diff --git a/examples/config/packages/asset_mapper.yaml b/examples/config/packages/asset_mapper.yaml new file mode 100644 index 0000000..d1ac653 --- /dev/null +++ b/examples/config/packages/asset_mapper.yaml @@ -0,0 +1,5 @@ +framework: + asset_mapper: + # The paths to make available to the asset mapper. + paths: + - assets/ diff --git a/examples/config/packages/cache.yaml b/examples/config/packages/cache.yaml new file mode 100644 index 0000000..6899b72 --- /dev/null +++ b/examples/config/packages/cache.yaml @@ -0,0 +1,19 @@ +framework: + cache: + # Unique name of your app: used to compute stable namespaces for cache keys. + #prefix_seed: your_vendor_name/app_name + + # The "app" cache stores to the filesystem by default. + # The data in this cache should persist between deploys. + # Other options include: + + # Redis + #app: cache.adapter.redis + #default_redis_provider: redis://localhost + + # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) + #app: cache.adapter.apcu + + # Namespaced pools use the above "app" backend by default + #pools: + #my.dedicated.cache: null diff --git a/examples/config/packages/debug.yaml b/examples/config/packages/debug.yaml new file mode 100644 index 0000000..ad874af --- /dev/null +++ b/examples/config/packages/debug.yaml @@ -0,0 +1,5 @@ +when@dev: + debug: + # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. + # See the "server:dump" command to start a new server. + dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" diff --git a/examples/config/packages/doctrine.yaml b/examples/config/packages/doctrine.yaml new file mode 100644 index 0000000..d42c52d --- /dev/null +++ b/examples/config/packages/doctrine.yaml @@ -0,0 +1,50 @@ +doctrine: + dbal: + url: '%env(resolve:DATABASE_URL)%' + + # IMPORTANT: You MUST configure your server version, + # either here or in the DATABASE_URL env var (see .env file) + #server_version: '16' + + profiling_collect_backtrace: '%kernel.debug%' + use_savepoints: true + orm: + auto_generate_proxy_classes: true + enable_lazy_ghost_objects: true + report_fields_where_declared: true + validate_xml_mapping: true + naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware + auto_mapping: true + mappings: + App: + type: attribute + is_bundle: false + dir: '%kernel.project_dir%/src/Entity' + prefix: 'App\Entity' + alias: App + +when@test: + doctrine: + dbal: + # "TEST_TOKEN" is typically set by ParaTest + dbname_suffix: '_test%env(default::TEST_TOKEN)%' + +when@prod: + doctrine: + orm: + auto_generate_proxy_classes: false + proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' + query_cache_driver: + type: pool + pool: doctrine.system_cache_pool + result_cache_driver: + type: pool + pool: doctrine.result_cache_pool + + framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/examples/config/packages/doctrine_migrations.yaml b/examples/config/packages/doctrine_migrations.yaml new file mode 100644 index 0000000..29231d9 --- /dev/null +++ b/examples/config/packages/doctrine_migrations.yaml @@ -0,0 +1,6 @@ +doctrine_migrations: + migrations_paths: + # namespace is arbitrary but should be different from App\Migrations + # as migrations classes should NOT be autoloaded + 'DoctrineMigrations': '%kernel.project_dir%/migrations' + enable_profiler: false diff --git a/examples/config/packages/framework.yaml b/examples/config/packages/framework.yaml new file mode 100644 index 0000000..877eb25 --- /dev/null +++ b/examples/config/packages/framework.yaml @@ -0,0 +1,16 @@ +# see https://symfony.com/doc/current/reference/configuration/framework.html +framework: + secret: '%env(APP_SECRET)%' + #csrf_protection: true + + # Note that the session will be started ONLY if you read or write from it. + session: true + + #esi: true + #fragments: true + +when@test: + framework: + test: true + session: + storage_factory_id: session.storage.factory.mock_file diff --git a/examples/config/packages/mailer.yaml b/examples/config/packages/mailer.yaml new file mode 100644 index 0000000..56a650d --- /dev/null +++ b/examples/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_DSN)%' diff --git a/examples/config/packages/messenger.yaml b/examples/config/packages/messenger.yaml new file mode 100644 index 0000000..587083a --- /dev/null +++ b/examples/config/packages/messenger.yaml @@ -0,0 +1,24 @@ +framework: + messenger: + failure_transport: failed + + transports: + # https://symfony.com/doc/current/messenger.html#transport-configuration + async: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + options: + use_notify: true + check_delayed_interval: 60000 + retry_strategy: + max_retries: 3 + multiplier: 2 + failed: 'doctrine://default?queue_name=failed' + # sync: 'sync://' + + routing: + Symfony\Component\Mailer\Messenger\SendEmailMessage: async + Symfony\Component\Notifier\Message\ChatMessage: async + Symfony\Component\Notifier\Message\SmsMessage: async + + # Route your messages to the transports + # 'App\Message\YourMessage': async diff --git a/examples/config/packages/monolog.yaml b/examples/config/packages/monolog.yaml new file mode 100644 index 0000000..9db7d8a --- /dev/null +++ b/examples/config/packages/monolog.yaml @@ -0,0 +1,62 @@ +monolog: + channels: + - deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists + +when@dev: + monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + channels: ["!event"] + # uncomment to get logging in your browser + # you may have to allow bigger header sizes in your Web server configuration + #firephp: + # type: firephp + # level: info + #chromephp: + # type: chromephp + # level: info + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine", "!console"] + +when@test: + monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + excluded_http_codes: [404, 405] + channels: ["!event"] + nested: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + +when@prod: + monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + excluded_http_codes: [404, 405] + buffer_size: 50 # How many messages should be saved? Prevent memory leaks + nested: + type: stream + path: php://stderr + level: debug + formatter: monolog.formatter.json + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine"] + deprecation: + type: stream + channels: [deprecation] + path: php://stderr + formatter: monolog.formatter.json diff --git a/examples/config/packages/notifier.yaml b/examples/config/packages/notifier.yaml new file mode 100644 index 0000000..d02f986 --- /dev/null +++ b/examples/config/packages/notifier.yaml @@ -0,0 +1,12 @@ +framework: + notifier: + chatter_transports: + texter_transports: + channel_policy: + # use chat/slack, chat/telegram, sms/twilio or sms/nexmo + urgent: ['email'] + high: ['email'] + medium: ['email'] + low: ['email'] + admin_recipients: + - { email: admin@example.com } diff --git a/examples/config/packages/routing.yaml b/examples/config/packages/routing.yaml new file mode 100644 index 0000000..8166181 --- /dev/null +++ b/examples/config/packages/routing.yaml @@ -0,0 +1,10 @@ +framework: + router: + # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. + # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands + #default_uri: http://localhost + +when@prod: + framework: + router: + strict_requirements: null diff --git a/examples/config/packages/security.yaml b/examples/config/packages/security.yaml new file mode 100644 index 0000000..367af25 --- /dev/null +++ b/examples/config/packages/security.yaml @@ -0,0 +1,39 @@ +security: + # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords + password_hashers: + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider + providers: + users_in_memory: { memory: null } + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + main: + lazy: true + provider: users_in_memory + + # activate different ways to authenticate + # https://symfony.com/doc/current/security.html#the-firewall + + # https://symfony.com/doc/current/security/impersonating_user.html + # switch_user: true + + # Easy way to control access for large sections of your site + # Note: Only the *first* access control that matches will be used + access_control: + # - { path: ^/admin, roles: ROLE_ADMIN } + # - { path: ^/profile, roles: ROLE_USER } + +when@test: + security: + password_hashers: + # By default, password hashers are resource intensive and take time. This is + # important to generate secure password hashes. In tests however, secure hashes + # are not important, waste resources and increase test times. The following + # reduces the work factor to the lowest possible values. + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: + algorithm: auto + cost: 4 # Lowest possible value for bcrypt + time_cost: 3 # Lowest possible value for argon + memory_cost: 10 # Lowest possible value for argon diff --git a/examples/config/packages/translation.yaml b/examples/config/packages/translation.yaml new file mode 100644 index 0000000..b3f8f9c --- /dev/null +++ b/examples/config/packages/translation.yaml @@ -0,0 +1,7 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en + providers: diff --git a/examples/config/packages/twig.yaml b/examples/config/packages/twig.yaml new file mode 100644 index 0000000..3f795d9 --- /dev/null +++ b/examples/config/packages/twig.yaml @@ -0,0 +1,6 @@ +twig: + file_name_pattern: '*.twig' + +when@test: + twig: + strict_variables: true diff --git a/examples/config/packages/validator.yaml b/examples/config/packages/validator.yaml new file mode 100644 index 0000000..dd47a6a --- /dev/null +++ b/examples/config/packages/validator.yaml @@ -0,0 +1,11 @@ +framework: + validation: + # Enables validator auto-mapping support. + # For instance, basic validation constraints will be inferred from Doctrine's metadata. + #auto_mapping: + # App\Entity\: [] + +when@test: + framework: + validation: + not_compromised_password: false diff --git a/examples/config/packages/web_profiler.yaml b/examples/config/packages/web_profiler.yaml new file mode 100644 index 0000000..b946111 --- /dev/null +++ b/examples/config/packages/web_profiler.yaml @@ -0,0 +1,17 @@ +when@dev: + web_profiler: + toolbar: true + intercept_redirects: false + + framework: + profiler: + only_exceptions: false + collect_serializer_data: true + +when@test: + web_profiler: + toolbar: false + intercept_redirects: false + + framework: + profiler: { collect: false } diff --git a/examples/config/preload.php b/examples/config/preload.php new file mode 100644 index 0000000..db37723 --- /dev/null +++ b/examples/config/preload.php @@ -0,0 +1,5 @@ + [ + 'path' => './assets/app.js', + 'entrypoint' => true, + ], + '@hotwired/stimulus' => [ + 'version' => '3.2.2', + ], + '@symfony/stimulus-bundle' => [ + 'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js', + ], + '@hotwired/turbo' => [ + 'version' => '7.3.0', + ], +]; diff --git a/examples/infrastructure/docker/docker-compose.builder.yml b/examples/infrastructure/docker/docker-compose.builder.yml new file mode 100644 index 0000000..f2b4807 --- /dev/null +++ b/examples/infrastructure/docker/docker-compose.builder.yml @@ -0,0 +1,25 @@ +version: '3.7' + +volumes: + builder-data: {} + +services: + builder: + build: + context: services/php + target: builder + depends_on: + - postgres + environment: + - COMPOSER_MEMORY_LIMIT=-1 + - UID=${USER_ID} + # The following list contains the common environment variables exposed by CI platforms + - GITHUB_ACTIONS + - CI # Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari + - CONTINUOUS_INTEGRATION # Travis CI, Cirrus CI + - BUILD_NUMBER # Jenkins, TeamCity + - RUN_ID # TaskCluster, dsari + volumes: + - "builder-data:/home/app" + - "${COMPOSER_CACHE_DIR}:/home/app/.composer/cache" + - "../..:/var/www:cached" diff --git a/examples/infrastructure/docker/docker-compose.docker-for-x.yml b/examples/infrastructure/docker/docker-compose.docker-for-x.yml new file mode 100644 index 0000000..dcc24b0 --- /dev/null +++ b/examples/infrastructure/docker/docker-compose.docker-for-x.yml @@ -0,0 +1,13 @@ +# This file is used only when using Docker for Mac or Windows + +version: '3.7' + +services: + router: + # Docker for Mac or Windows does not support the "network_mode=host" + # So we use the default network mode and we map all needed ports + network_mode: "${PROJECT_NAME}_default" + ports: + - "80:80" + - "443:443" + - "8080:8080" diff --git a/examples/infrastructure/docker/docker-compose.worker.yml b/examples/infrastructure/docker/docker-compose.worker.yml new file mode 100644 index 0000000..a8a17d1 --- /dev/null +++ b/examples/infrastructure/docker/docker-compose.worker.yml @@ -0,0 +1,20 @@ +version: '3.7' + +# this is a template to factorize the service definitions +x-services-templates: + worker_base: &worker_base + build: + context: services/php + target: worker + depends_on: + - postgres + #- rabbitmq + volumes: + - "../..:/var/www:cached" + labels: + - "docker-starter.worker.${PROJECT_NAME}=true" + +# services: +# worker_messenger: +# <<: *worker_base +# command: php -d memory_limit=1G /var/www/bin/console messenger:consume async --memory-limit=128M diff --git a/examples/infrastructure/docker/docker-compose.yml b/examples/infrastructure/docker/docker-compose.yml new file mode 100644 index 0000000..230a21a --- /dev/null +++ b/examples/infrastructure/docker/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.7' + +volumes: + postgres-data: {} + +services: + router: + build: services/router + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "./services/router/certs:/etc/ssl/certs" + network_mode: host + + frontend: + build: + context: services/php + target: frontend + depends_on: + - postgres + volumes: + - "../..:/var/www:cached" + environment: + - "PHP_VERSION=${PHP_VERSION}" + labels: + - "traefik.enable=true" + - "traefik.http.routers.${PROJECT_NAME}-frontend.rule=Host(${PROJECT_DOMAINS})" + - "traefik.http.routers.${PROJECT_NAME}-frontend.tls=true" + - "traefik.http.routers.${PROJECT_NAME}-frontend-unsecure.rule=Host(${PROJECT_DOMAINS})" + # Comment the next line to be able to access frontend via HTTP instead of HTTPS + - "traefik.http.routers.${PROJECT_NAME}-frontend-unsecure.middlewares=redirect-to-https@file" + + postgres: + image: postgres:16 + environment: + - POSTGRES_USER=app + - POSTGRES_PASSWORD=app + volumes: + - postgres-data:/var/lib/postgresql/data diff --git a/examples/infrastructure/docker/services/php/Dockerfile b/examples/infrastructure/docker/services/php/Dockerfile new file mode 100644 index 0000000..c4ef231 --- /dev/null +++ b/examples/infrastructure/docker/services/php/Dockerfile @@ -0,0 +1,121 @@ +# hadolint global ignore=DL3008 + +FROM debian:12.5-slim as php-base + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + && curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb \ + && dpkg -i /tmp/debsuryorg-archive-keyring.deb \ + && echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php bookworm main" > /etc/apt/sources.list.d/sury.list \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + procps \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +ARG PHP_VERSION + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + "php${PHP_VERSION}-apcu" \ + "php${PHP_VERSION}-bcmath" \ + "php${PHP_VERSION}-cli" \ + "php${PHP_VERSION}-common" \ + "php${PHP_VERSION}-curl" \ + "php${PHP_VERSION}-iconv" \ + "php${PHP_VERSION}-intl" \ + "php${PHP_VERSION}-mbstring" \ + "php${PHP_VERSION}-pgsql" \ + "php${PHP_VERSION}-uuid" \ + "php${PHP_VERSION}-xml" \ + "php${PHP_VERSION}-zip" \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +# Fake user to maps with the one on the host +ARG USER_ID +COPY entrypoint / +RUN addgroup --gid $USER_ID app && \ + adduser --system --uid $USER_ID --home /home/app --shell /bin/bash app && \ + curl -Ls https://github.com/tianon/gosu/releases/download/1.17/gosu-amd64 | \ + install /dev/stdin /usr/local/bin/gosu && \ + sed "s/{{ application_user }}/app/g" -i /entrypoint + +# Configuration +COPY base/php-configuration /etc/php/${PHP_VERSION} + +WORKDIR /var/www +ENTRYPOINT [ "/entrypoint" ] + +FROM php-base as frontend + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + nginx \ + "php${PHP_VERSION}-fpm" \ + runit \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* \ + && rm -r "/etc/php/${PHP_VERSION}/fpm/pool.d/" + +RUN useradd -s /bin/false nginx + +COPY frontend/php-configuration /etc/php/${PHP_VERSION} +COPY frontend/etc/nginx/. /etc/nginx/ +COPY frontend/etc/service/. /etc/service/ + +RUN phpenmod app-default \ + && phpenmod app-fpm + +EXPOSE 80 + +CMD ["runsvdir", "-P", "/etc/service"] + +FROM php-base as worker + +FROM php-base as builder + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ARG NODEJS_VERSION=20.x +RUN curl -s https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODEJS_VERSION} nodistro main" > /etc/apt/sources.list.d/nodesource.list + +# Default toys +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git \ + make \ + nodejs \ + sudo \ + unzip \ + && apt-get clean \ + && npm install -g yarn@1.22 \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +# Config +COPY builder/etc/. /etc/ +COPY builder/php-configuration /etc/php/${PHP_VERSION} +RUN adduser app sudo \ + && mkdir /var/log/php \ + && chmod 777 /var/log/php \ + && phpenmod app-default \ + && phpenmod app-builder + +# Composer +COPY --from=composer/composer:2.7.1 /usr/bin/composer /usr/bin/composer +RUN mkdir -p "/home/app/.composer/cache" \ + && chown app: /home/app/.composer -R + +# Third party tools +ENV PATH="$PATH:/var/www/tools/bin" + +WORKDIR /var/www diff --git a/examples/infrastructure/docker/services/php/base/php-configuration/mods-available/app-default.ini b/examples/infrastructure/docker/services/php/base/php-configuration/mods-available/app-default.ini new file mode 100644 index 0000000..17d01e4 --- /dev/null +++ b/examples/infrastructure/docker/services/php/base/php-configuration/mods-available/app-default.ini @@ -0,0 +1,27 @@ +; priority=30 +[PHP] +short_open_tag = Off +memory_limit = 512M +error_reporting = E_ALL +display_errors = On +display_startup_errors = On +error_log = /proc/self/fd/2 +log_errors = On +log_errors_max_len = 0 +max_execution_time = 0 +always_populate_raw_post_data = -1 +upload_max_filesize = 20M +post_max_size = 20M +[Date] +date.timezone = UTC +[Phar] +phar.readonly = Off +[opcache] +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +realpath_cache_size=4096K +realpath_cache_ttl=600 +opcache.error_log = /proc/self/fd/2 +[apc] +apc.enabled=1 +apc.enable_cli=1 diff --git a/examples/infrastructure/docker/services/php/builder/etc/sudoers.d/sudo b/examples/infrastructure/docker/services/php/builder/etc/sudoers.d/sudo new file mode 100644 index 0000000..b835421 --- /dev/null +++ b/examples/infrastructure/docker/services/php/builder/etc/sudoers.d/sudo @@ -0,0 +1 @@ +%sudo ALL=(ALL) NOPASSWD: ALL diff --git a/examples/infrastructure/docker/services/php/builder/php-configuration/mods-available/app-builder.ini b/examples/infrastructure/docker/services/php/builder/php-configuration/mods-available/app-builder.ini new file mode 100644 index 0000000..3016083 --- /dev/null +++ b/examples/infrastructure/docker/services/php/builder/php-configuration/mods-available/app-builder.ini @@ -0,0 +1,5 @@ +; priority=40 +[PHP] +error_log = /var/log/php/error.log +[opcache] +opcache.error_log = /var/log/php/opcache.log diff --git a/examples/infrastructure/docker/services/php/entrypoint b/examples/infrastructure/docker/services/php/entrypoint new file mode 100755 index 0000000..1f15e84 --- /dev/null +++ b/examples/infrastructure/docker/services/php/entrypoint @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e +set -u + +if [ $(id -u) != 0 ]; then + echo "Running this image as non root is not allowed" + exit 1 +fi + +: "${UID:=0}" +: "${GID:=${UID}}" + +if [ "$#" = 0 ]; then + set -- "$(command -v bash 2>/dev/null || command -v sh)" -l +fi + +if [ "$UID" != 0 ]; then + usermod -u "$UID" "{{ application_user }}" >/dev/null 2>/dev/null && { + groupmod -g "$GID" "{{ application_user }}" >/dev/null 2>/dev/null || + usermod -a -G "$GID" "{{ application_user }}" >/dev/null 2>/dev/null + } + set -- gosu "${UID}:${GID}" "${@}" +fi + +exec "$@" diff --git a/examples/infrastructure/docker/services/php/frontend/etc/nginx/environments b/examples/infrastructure/docker/services/php/frontend/etc/nginx/environments new file mode 100644 index 0000000..e69de29 diff --git a/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf b/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf new file mode 100644 index 0000000..8e939f7 --- /dev/null +++ b/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf @@ -0,0 +1,74 @@ +user nginx; +pid /var/run/nginx.pid; +daemon off; +error_log /proc/self/fd/2; +include /etc/nginx/modules-enabled/*.conf; + +http { + access_log /proc/self/fd/1; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + include /etc/nginx/mime.types; + default_type application/octet-stream; + client_max_body_size 20m; + server_tokens off; + + gzip on; + gzip_disable "msie6"; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + + server { + listen 0.0.0.0:80; + root /var/www/public; + + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg)$ { + access_log off; + add_header Cache-Control "no-cache"; + } + + # Remove this block if you want to access to PHP FPM monitoring + # dashboard (on URL: /php-fpm-status). WARNING: on production, you must + # secure this page (by user IP address, with a password, for example) + location ~ ^/php-fpm-status$ { + deny all; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_pass 127.0.0.1:9000; + } + + location / { + # try to serve file directly, fallback to index.php + try_files $uri /index.php$is_args$args; + } + + location ~ ^/index\.php(/|$) { + fastcgi_pass 127.0.0.1:9000; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + + include fastcgi_params; + include environments; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTPS on; + fastcgi_param SERVER_NAME $http_host; + # # Uncomment if you want to use /php-fpm-status endpoint **with** + # # real request URI. It may have some side effects, that's why it's + # # commented by default + # fastcgi_param SCRIPT_NAME $request_uri; + } + + error_log /proc/self/fd/2; + access_log /proc/self/fd/1; + } +} + +events {} diff --git a/examples/infrastructure/docker/services/php/frontend/etc/service/nginx/run b/examples/infrastructure/docker/services/php/frontend/etc/service/nginx/run new file mode 100755 index 0000000..14cd019 --- /dev/null +++ b/examples/infrastructure/docker/services/php/frontend/etc/service/nginx/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/nginx diff --git a/examples/infrastructure/docker/services/php/frontend/etc/service/php-fpm/run b/examples/infrastructure/docker/services/php/frontend/etc/service/php-fpm/run new file mode 100755 index 0000000..1bcdb2b --- /dev/null +++ b/examples/infrastructure/docker/services/php/frontend/etc/service/php-fpm/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/php-fpm${PHP_VERSION} -y /etc/php/${PHP_VERSION}/fpm/php-fpm.conf -O diff --git a/examples/infrastructure/docker/services/php/frontend/php-configuration/fpm/php-fpm.conf b/examples/infrastructure/docker/services/php/frontend/php-configuration/fpm/php-fpm.conf new file mode 100644 index 0000000..38b5901 --- /dev/null +++ b/examples/infrastructure/docker/services/php/frontend/php-configuration/fpm/php-fpm.conf @@ -0,0 +1,19 @@ +[global] +pid = /var/run/php-fpm.pid +error_log = /proc/self/fd/2 +daemonize = no + +[www] +user = app +group = app +listen = 127.0.0.1:9000 +pm = dynamic +pm.max_children = 25 +pm.start_servers = 2 +pm.min_spare_servers = 2 +pm.max_spare_servers = 3 +pm.max_requests = 500 +pm.status_path = /php-fpm-status +clear_env = no +request_terminate_timeout = 120s +catch_workers_output = yes diff --git a/examples/infrastructure/docker/services/php/frontend/php-configuration/mods-available/app-fpm.ini b/examples/infrastructure/docker/services/php/frontend/php-configuration/mods-available/app-fpm.ini new file mode 100644 index 0000000..f3f541d --- /dev/null +++ b/examples/infrastructure/docker/services/php/frontend/php-configuration/mods-available/app-fpm.ini @@ -0,0 +1,5 @@ +; priority=40 +[PHP] +expose_php = off +memory_limit = 128M +max_execution_time = 30 diff --git a/examples/infrastructure/docker/services/router/Dockerfile b/examples/infrastructure/docker/services/router/Dockerfile new file mode 100644 index 0000000..cbd3f79 --- /dev/null +++ b/examples/infrastructure/docker/services/router/Dockerfile @@ -0,0 +1,5 @@ +FROM traefik:v3.0 + +COPY traefik /etc/traefik + +VOLUME [ "/etc/ssl/certs" ] diff --git a/examples/infrastructure/docker/services/router/certs/.gitkeep b/examples/infrastructure/docker/services/router/certs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/infrastructure/docker/services/router/generate-ssl.sh b/examples/infrastructure/docker/services/router/generate-ssl.sh new file mode 100755 index 0000000..f9d49fb --- /dev/null +++ b/examples/infrastructure/docker/services/router/generate-ssl.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# Script used in dev to generate a basic SSL cert + +BASE=$(dirname $0) + +CERTS_DIR=$BASE/certs + +rm -rf $CERTS_DIR +mkdir -p $CERTS_DIR + +openssl req -x509 -sha256 -newkey rsa:4096 \ + -keyout $CERTS_DIR/key.pem \ + -out $CERTS_DIR/cert.pem \ + -days 3650 -nodes -config \ + $BASE/openssl.cnf diff --git a/examples/infrastructure/docker/services/router/openssl.cnf b/examples/infrastructure/docker/services/router/openssl.cnf new file mode 100644 index 0000000..ff68b84 --- /dev/null +++ b/examples/infrastructure/docker/services/router/openssl.cnf @@ -0,0 +1,19 @@ +# Configuration used in dev to generate a basic SSL cert +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +x509_extensions = v3_req + +[v3_req] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:true +subjectAltName = @alt_names + +[dn] +CN=app.test + +[alt_names] +DNS.1 = app.test diff --git a/examples/infrastructure/docker/services/router/traefik/dynamic_conf.yaml b/examples/infrastructure/docker/services/router/traefik/dynamic_conf.yaml new file mode 100644 index 0000000..5680885 --- /dev/null +++ b/examples/infrastructure/docker/services/router/traefik/dynamic_conf.yaml @@ -0,0 +1,12 @@ +tls: + stores: + default: + defaultCertificate: + certFile: /etc/ssl/certs/cert.pem + keyFile: /etc/ssl/certs/key.pem + +http: + middlewares: + redirect-to-https: + redirectScheme: + scheme: https diff --git a/examples/infrastructure/docker/services/router/traefik/traefik.yaml b/examples/infrastructure/docker/services/router/traefik/traefik.yaml new file mode 100644 index 0000000..a4c101a --- /dev/null +++ b/examples/infrastructure/docker/services/router/traefik/traefik.yaml @@ -0,0 +1,28 @@ +global: + checkNewVersion: false + sendAnonymousUsage: false + +providers: + docker: + exposedByDefault: false + file: + filename: /etc/traefik/dynamic_conf.yaml + +# # Uncomment get all DEBUG logs +#log: +# level: "DEBUG" + +# # Uncomment to view all access logs +#accessLog: {} + +api: + dashboard: true + insecure: true # No authentication are required + +entryPoints: + http: + address: ":80" + https: + address: ":443" + traefik: # this one exists by default + address: ":8080" diff --git a/examples/migrations/.gitignore b/examples/migrations/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/examples/phpstan.neon b/examples/phpstan.neon new file mode 100644 index 0000000..2db6d86 --- /dev/null +++ b/examples/phpstan.neon @@ -0,0 +1,31 @@ +parameters: + level: 6 + paths: + - src + - public + - castor.php + - .castor/ + scanFiles: + - .castor.stub.php + tmpDir: tools/phpstan/var + inferPrivatePropertyTypeFromConstructor: true + + symfony: + container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml' + + typeAliases: + ContextData: ''' + array{ + project_name: string, + root_domain: string, + extra_domains: string[], + php_version: string, + docker_compose_files: string[], + macos: bool, + power_shell: bool, + user_id: int, + root_dir: string, + env: string, + composer_cache_dir: string, + } + ''' diff --git a/examples/phpunit.xml.dist b/examples/phpunit.xml.dist new file mode 100644 index 0000000..6c4bfed --- /dev/null +++ b/examples/phpunit.xml.dist @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + tests + + + + + + src + + + + + + + + + + diff --git a/examples/public/index.php b/examples/public/index.php new file mode 100644 index 0000000..7fbc8cf --- /dev/null +++ b/examples/public/index.php @@ -0,0 +1,9 @@ +render('homepage/index.html.twig', [ + 'controller_name' => 'HomepageController', + ]); + } +} diff --git a/examples/src/Entity/.gitignore b/examples/src/Entity/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/examples/src/Kernel.php b/examples/src/Kernel.php new file mode 100644 index 0000000..779cd1f --- /dev/null +++ b/examples/src/Kernel.php @@ -0,0 +1,11 @@ + + + + + {% block title %}Welcome!{% endblock %} + + {% block stylesheets %} + {% endblock %} + + {% block javascripts %} + {% block importmap %}{{ importmap('app') }}{% endblock %} + {% endblock %} + + + {% block body %}{% endblock %} + + diff --git a/examples/tests/bootstrap.php b/examples/tests/bootstrap.php new file mode 100644 index 0000000..5160486 --- /dev/null +++ b/examples/tests/bootstrap.php @@ -0,0 +1,11 @@ +bootEnv(dirname(__DIR__) . '/.env'); +} diff --git a/examples/tools/bin/php-cs-fixer b/examples/tools/bin/php-cs-fixer new file mode 120000 index 0000000..8b6029f --- /dev/null +++ b/examples/tools/bin/php-cs-fixer @@ -0,0 +1 @@ +../php-cs-fixer/vendor/bin/php-cs-fixer \ No newline at end of file diff --git a/examples/tools/bin/phpstan b/examples/tools/bin/phpstan new file mode 120000 index 0000000..c10edfd --- /dev/null +++ b/examples/tools/bin/phpstan @@ -0,0 +1 @@ +../phpstan/vendor/bin/phpstan \ No newline at end of file diff --git a/examples/tools/php-cs-fixer/.gitignore b/examples/tools/php-cs-fixer/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/examples/tools/php-cs-fixer/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/examples/tools/php-cs-fixer/composer.json b/examples/tools/php-cs-fixer/composer.json new file mode 100644 index 0000000..c216598 --- /dev/null +++ b/examples/tools/php-cs-fixer/composer.json @@ -0,0 +1,12 @@ +{ + "type": "project", + "require": { + "friendsofphp/php-cs-fixer": "^3.47.1" + }, + "config": { + "platform": { + "php": "8.1" + }, + "sort-packages": true + } +} diff --git a/examples/tools/php-cs-fixer/composer.lock b/examples/tools/php-cs-fixer/composer.lock new file mode 100644 index 0000000..6e1c4b1 --- /dev/null +++ b/examples/tools/php-cs-fixer/composer.lock @@ -0,0 +1,1845 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "74ae7a6662eee7cb4b9ed2a50b72ec98", + "packages": [ + { + "name": "composer/pcre", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-10-11T07:11:09+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.47.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "173c60d1eff911c9c54322704623a45561d3241d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/173c60d1eff911c9c54322704623a45561d3241d", + "reference": "173c60d1eff911c9c54322704623a45561d3241d", + "shasum": "" + }, + "require": { + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.0", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", + "phpunit/phpunit": "^9.6 || ^10.5.5", + "symfony/yaml": "^5.4 || ^6.0 || ^7.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", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.47.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-01-16T18:54:21+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T10:55:06+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "0254811a143e6bc6c8deea08b589a7e68a37f625" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/0254811a143e6bc6c8deea08b589a7e68a37f625", + "reference": "0254811a143e6bc6c8deea08b589a7e68a37f625", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^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" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-10T16:15:48+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "e95216850555cd55e71b857eb9d6c2674124603a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e95216850555cd55e71b857eb9d6c2674124603a", + "reference": "e95216850555cd55e71b857eb9d6c2674124603a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-27T22:16:42+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.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": "2023-07-26T17:27:13+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.4.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": "2023-10-31T17:30:12+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "22301f0e7fdeaacc14318928612dee79be99860e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22301f0e7fdeaacc14318928612dee79be99860e", + "reference": "22301f0e7fdeaacc14318928612dee79be99860e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.4.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": "2023-08-08T10:16:24+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.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": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "c4b1ef0bc80533d87a2e969806172f1c2a980241" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/c4b1ef0bc80533d87a2e969806172f1c2a980241", + "reference": "c4b1ef0bc80533d87a2e969806172f1c2a980241", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-22T16:42:54+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-26T14:02:43+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.4.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": "2023-02-16T10:14:28+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/7cb80bc10bfcdf6b5492741c0b9357dac66940bc", + "reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-10T16:15:48+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +} diff --git a/examples/tools/phpstan/.gitignore b/examples/tools/phpstan/.gitignore new file mode 100644 index 0000000..31b30cc --- /dev/null +++ b/examples/tools/phpstan/.gitignore @@ -0,0 +1,2 @@ +/var/ +/vendor/ diff --git a/examples/tools/phpstan/composer.json b/examples/tools/phpstan/composer.json new file mode 100644 index 0000000..2c38af5 --- /dev/null +++ b/examples/tools/phpstan/composer.json @@ -0,0 +1,17 @@ +{ + "type": "project", + "require": { + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.56", + "phpstan/phpstan-symfony": "^1.3.7" + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + }, + "platform": { + "php": "8.1" + }, + "sort-packages": true + } +} diff --git a/examples/tools/phpstan/composer.lock b/examples/tools/phpstan/composer.lock new file mode 100644 index 0000000..e91f1bc --- /dev/null +++ b/examples/tools/phpstan/composer.lock @@ -0,0 +1,200 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e2bde0ca0bcc114a0ba6528759a1ec8e", + "packages": [ + { + "name": "phpstan/extension-installer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" + }, + "time": "2023-05-24T08:59:17+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.10.56", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "27816a01aea996191ee14d010f325434c0ee76fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/27816a01aea996191ee14d010f325434c0ee76fa", + "reference": "27816a01aea996191ee14d010f325434c0ee76fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2024-01-15T10:43:00+00:00" + }, + { + "name": "phpstan/phpstan-symfony", + "version": "1.3.7", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-symfony.git", + "reference": "ef7db637be9b85fa00278fc3477ac66abe8eb7d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/ef7db637be9b85fa00278fc3477ac66abe8eb7d1", + "reference": "ef7db637be9b85fa00278fc3477ac66abe8eb7d1", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.10.36" + }, + "conflict": { + "symfony/framework-bundle": "<3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.3.11", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^8.5.29 || ^9.5", + "psr/container": "1.0 || 1.1.1", + "symfony/config": "^5.4 || ^6.1", + "symfony/console": "^5.4 || ^6.1", + "symfony/dependency-injection": "^5.4 || ^6.1", + "symfony/form": "^5.4 || ^6.1", + "symfony/framework-bundle": "^5.4 || ^6.1", + "symfony/http-foundation": "^5.4 || ^6.1", + "symfony/messenger": "^5.4", + "symfony/polyfill-php80": "^1.24", + "symfony/serializer": "^5.4", + "symfony/service-contracts": "^2.2.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lukáš Unger", + "email": "looky.msc@gmail.com", + "homepage": "https://lookyman.net" + } + ], + "description": "Symfony Framework extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-symfony/issues", + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.3.7" + }, + "time": "2024-01-10T21:54:42+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +} diff --git a/examples/translations/.gitignore b/examples/translations/.gitignore new file mode 100644 index 0000000..e69de29 From da7f1798dac1020cb27b90ff05a6cf34b2596edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 23:34:49 +0100 Subject: [PATCH 19/32] bundle: enhance the configuration parsing + add AliasForArgument --- phpstan-baseline.neon | 7 ++- src/Metrics/Collector/DogStatsD.php | 1 - src/Metrics/Collector/Graphite.php | 20 ++++----- src/Metrics/Collector/Telegraf.php | 13 +++--- src/Metrics/Utils/Box.php | 2 + .../BeberleiMetricsExtension.php | 45 ++++++++++++------- .../DependencyInjection/Configuration.php | 14 ++++-- 7 files changed, 64 insertions(+), 38 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a5dbf7a..842fe65 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,12 @@ parameters: ignoreErrors: - - message: "#^Variable \\$name might not be defined\\.$#" + message: "#^Parameter \\#1 \\$callback of function set_error_handler expects \\(callable\\(int, string, string, int\\)\\: bool\\)\\|null, Closure\\(\\)\\: null given\\.$#" + count: 1 + path: src/Metrics/Utils/Box.php + + - + message: "#^Cannot access offset 'null' on 0\\|0\\.0\\|''\\|'0'\\|array\\{\\}\\|false\\|null\\.$#" count: 1 path: src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php diff --git a/src/Metrics/Collector/DogStatsD.php b/src/Metrics/Collector/DogStatsD.php index 0604ecf..58f87f0 100644 --- a/src/Metrics/Collector/DogStatsD.php +++ b/src/Metrics/Collector/DogStatsD.php @@ -61,7 +61,6 @@ public function flush(): void private function doFlush(): void { - $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { diff --git a/src/Metrics/Collector/Graphite.php b/src/Metrics/Collector/Graphite.php index 9ff5930..b0d7ccd 100644 --- a/src/Metrics/Collector/Graphite.php +++ b/src/Metrics/Collector/Graphite.php @@ -58,6 +58,16 @@ public function flush(): void Box::box($this->doFlush(...)); } + public function push(string $variable, int|float $value, ?int $time = null): void + { + $this->data[] = sprintf( + \is_float($value) ? "%s %.18f %d\n" : "%s %d %d\n", + $variable, + $value, + $time ?: time() + ); + } + private function doFlush(): void { $fp = fsockopen($this->protocol . '://' . $this->host, $this->port); @@ -74,14 +84,4 @@ private function doFlush(): void $this->data = []; } - - public function push(string $variable, int|float $value, ?int $time = null): void - { - $this->data[] = sprintf( - \is_float($value) ? "%s %.18f %d\n" : "%s %d %d\n", - $variable, - $value, - $time ?: time() - ); - } } diff --git a/src/Metrics/Collector/Telegraf.php b/src/Metrics/Collector/Telegraf.php index e2b2905..773e863 100644 --- a/src/Metrics/Collector/Telegraf.php +++ b/src/Metrics/Collector/Telegraf.php @@ -71,9 +71,14 @@ public function flush(): void Box::box($this->doFlush(...)); } - private function doFlush(): void + public function setTags(array $tags): void { + $this->tags = http_build_query($tags, '', ','); + $this->tags = \strlen($this->tags) > 0 ? ',' . $this->tags : $this->tags; + } + private function doFlush(): void + { $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); if (!$fp) { @@ -88,10 +93,4 @@ private function doFlush(): void $this->data = []; } - - public function setTags(array $tags): void - { - $this->tags = http_build_query($tags, '', ','); - $this->tags = \strlen($this->tags) > 0 ? ',' . $this->tags : $this->tags; - } } diff --git a/src/Metrics/Utils/Box.php b/src/Metrics/Utils/Box.php index b191fe5..ffe2fb1 100644 --- a/src/Metrics/Utils/Box.php +++ b/src/Metrics/Utils/Box.php @@ -15,5 +15,7 @@ public static function box(callable $callable): mixed } finally { restore_error_handler(); } + + return null; } } diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index b9a209e..7f2ada3 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -3,9 +3,11 @@ namespace Beberlei\Bundle\MetricsBundle\DependencyInjection; use Beberlei\Metrics\Collector\CollectorInterface; +use Prometheus\Collector; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -20,19 +22,31 @@ public function load(array $configs, ContainerBuilder $container): void $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('metrics.xml'); + if (!$config['collectors']) { + $config['collectors']['null'] = [ + 'type' => 'null', + ]; + } foreach ($config['collectors'] as $name => $colConfig) { $definition = $this->createCollector($colConfig['type'], $colConfig); $container->setDefinition('beberlei_metrics.collector.' . $name, $definition); + $container->registerAliasForArgument('beberlei_metrics.collector.' . $name, CollectorInterface::class, $name); } - if ($config['default'] && $container->hasDefinition('beberlei_metrics.collector.' . $config['default'])) { + if ($config['default']) { + if (!$container->hasDefinition('beberlei_metrics.collector.' . $config['default'])) { + throw new InvalidArgumentException(sprintf('The default collector "%s" does not exist.', $config['default'])); + } $name = $config['default']; - } elseif (1 !== \count($config['collectors'])) { - throw new \LogicException('You should select a default collector'); + } elseif (1 === \count($config['collectors'])) { + $name = key($config['collectors']); + } else { + throw new InvalidArgumentException('No default collector is configured and there is more than one collector. Please define a default collector'); } - $container->setAlias('beberlei_metrics.collector', 'beberlei_metrics.collector.' . $name); - $container->setAlias(CollectorInterface::class, 'beberlei_metrics.collector'); + if ($name) { + $container->setAlias(CollectorInterface::class, 'beberlei_metrics.collector.' . $name); + } } private function createCollector(string $type, array $config): ChildDefinition @@ -42,8 +56,9 @@ private function createCollector(string $type, array $config): ChildDefinition // Theses listeners should be as late as possible $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'kernel.terminate']); $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'console.terminate']); + $definition->addTag(CollectorInterface::class); - if (\count($config['tags']) > 0) { + if ($config['tags'] ?? []) { $definition->addMethodCall('setTags', [$config['tags']]); } @@ -54,9 +69,9 @@ private function createCollector(string $type, array $config): ChildDefinition return $definition; case 'graphite': - $definition->replaceArgument(0, $config['host'] ?: 'localhost'); - $definition->replaceArgument(1, $config['port'] ?: 2003); - $definition->replaceArgument(2, $config['protocol'] ?: 'tcp'); + $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(1, $config['port'] ?? 2003); + $definition->replaceArgument(2, $config['protocol'] ?? 'tcp'); return $definition; case 'influxdb': @@ -64,7 +79,7 @@ private function createCollector(string $type, array $config): ChildDefinition return $definition; case 'librato': - $definition->replaceArgument(1, $config['source']); + $definition->replaceArgument(1, $config['host']); $definition->replaceArgument(2, $config['username']); $definition->replaceArgument(3, $config['password']); @@ -80,19 +95,19 @@ private function createCollector(string $type, array $config): ChildDefinition return $definition; case 'statsd': case 'dogstatsd': - $definition->replaceArgument(0, $config['host'] ?: 'localhost'); - $definition->replaceArgument(1, $config['port'] ?: 8125); + $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(1, $config['port'] ?? 8125); $definition->replaceArgument(2, (string) $config['prefix']); return $definition; case 'telegraf': - $definition->replaceArgument(0, $config['host'] ?: 'localhost'); - $definition->replaceArgument(1, $config['port'] ?: 8125); + $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(1, $config['port'] ?? 8125); $definition->replaceArgument(2, (string) $config['prefix']); return $definition; default: - throw new \InvalidArgumentException(sprintf('The type "%s" is not supported', $type)); + throw new \InvalidArgumentException(sprintf('The type "%s" is not supported.', $type)); } } } diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index cbdf163..239ab05 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -21,11 +21,17 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultNull() ->end() ->arrayNode('collectors') - ->isRequired() ->useAttributeAsKey('name') ->prototype('array') ->children() - ->scalarNode('type')->isRequired()->end() + ->scalarNode('type') + ->isRequired() + ->validate() + ->ifTrue(static fn ($v) => !\is_string($v)) + ->thenInvalid('The type must be a string got "%s".') + ->end() + + ->end() ->scalarNode('connection')->defaultNull()->end() ->scalarNode('file')->defaultNull()->end() ->scalarNode('host')->defaultNull()->end() @@ -44,8 +50,8 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['source'])) - ->thenInvalid('The source has to be specified to use a Librato') + ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['host'])) + ->thenInvalid('The host has to be specified to use a Librato') ->end() ->validate() ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['username'])) From 2163463dda5764b69a4623c52064d0e285f40bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 23:44:47 +0100 Subject: [PATCH 20/32] collector: drop librato integration --- README.md | 12 ---- composer.json | 2 - src/Metrics/Collector/Librato.php | 66 ------------------- src/Metrics/Factory.php | 25 ------- .../BeberleiMetricsExtension.php | 6 -- .../DependencyInjection/Configuration.php | 12 ---- .../Resources/config/metrics.xml | 11 ---- tests/Metrics/FactoryTest.php | 5 -- .../BeberleiMetricsExtensionTest.php | 24 ------- 9 files changed, 163 deletions(-) delete mode 100644 src/Metrics/Collector/Librato.php diff --git a/README.md b/README.md index 27e3b03..f160c04 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Currently supported backends: * Graphite * InfluxDB * Telegraf -* Librato * Logger (Psr\Log\LoggerInterface) * Null (Dummy that does nothing) * Prometheus @@ -70,12 +69,6 @@ $collector->flush(); 'foo.beberlei.de', - 'username' => 'foo', - 'password' => 'bar', -)); - $null = \Beberlei\Metrics\Factory::create('null'); ``` @@ -106,11 +99,6 @@ beberlei_metrics: collectors: foo: type: statsd - librato: - type: librato - username: foo - password: bar - source: hermes10 dbal: type: doctrine_dbal connection: metrics # using the connection named "metrics" diff --git a/composer.json b/composer.json index faf7084..7646e0a 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,6 @@ "jimdo/prometheus_client_php": "^0.5.1", "symfony/config": "^5.4 || ^6.4 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", - "symfony/http-client": "^5.4 || ^6.4 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", "symfony/phpunit-bridge": "^6.4.4 || ^7.0" }, @@ -50,7 +49,6 @@ }, "suggest": { "corley/influxdb-sdk": "For InfluxDB integration", - "kriswallsmith/buzz": "For Librato integration", "jimdo/prometheus_client_php": "For Prometheus integration" }, "config": { diff --git a/src/Metrics/Collector/Librato.php b/src/Metrics/Collector/Librato.php deleted file mode 100644 index 3830a58..0000000 --- a/src/Metrics/Collector/Librato.php +++ /dev/null @@ -1,66 +0,0 @@ - [], 'gauges' => []]; - - public function __construct( - private readonly HttpClientInterface $httpClient, - private readonly string $source, - private readonly string $username, - private readonly string $password - ) { - } - - public function measure(string $variable, int $value, array $tags = []): void - { - $this->data['gauges'][] = ['source' => $this->source, 'name' => $variable, 'value' => $value]; - } - - public function increment(string $variable, array $tags = []): void - { - $this->data['counters'][] = ['source' => $this->source, 'name' => $variable, 'value' => 1]; - } - - public function decrement(string $variable, array $tags = []): void - { - $this->data['counters'][] = ['source' => $this->source, 'name' => $variable, 'value' => -1]; - } - - public function timing(string $variable, int $time, array $tags = []): void - { - $this->data['gauges'][] = ['source' => $this->source, 'name' => $variable, 'value' => $time]; - } - - public function flush(): void - { - if (!$this->data['gauges'] && !$this->data['counters']) { - return; - } - - try { - $this->httpClient->request('POST', 'https://metrics-api.librato.com/v1/metrics', [ - 'auth_basic' => [$this->username, $this->password], - 'json' => $this->data, - ]); - $this->data = ['gauges' => [], 'counters' => []]; - } catch (ExceptionInterface) { - } - } -} diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 3016ce3..95aa387 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -18,19 +18,14 @@ use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; use Beberlei\Metrics\Collector\InfluxDB; -use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; use Beberlei\Metrics\Collector\Prometheus; use Beberlei\Metrics\Collector\StatsD; use Beberlei\Metrics\Collector\Telegraf; -use Symfony\Component\HttpClient\HttpClient; -use Symfony\Contracts\HttpClient\HttpClientInterface; abstract class Factory { - private static HttpClientInterface $httpClient; - /** * @throws MetricsException */ @@ -115,21 +110,6 @@ public static function create(string $type, array $options = []): CollectorInter return new Graphite($options['host'], $options['port']); - case 'librato': - if (!isset($options['hostname'])) { - throw new MetricsException('Hostname is required for librato collector.'); - } - - if (!isset($options['username'])) { - throw new MetricsException('No username given for librato collector.'); - } - - if (!isset($options['password'])) { - throw new MetricsException('No password given for librato collector.'); - } - - return new Librato(self::getHttpClient(), $options['hostname'], $options['username'], $options['password']); - case 'doctrine_dbal': if (!isset($options['connection'])) { throw new MetricsException('connection is required for Doctrine DBAL collector.'); @@ -167,9 +147,4 @@ public static function create(string $type, array $options = []): CollectorInter throw new MetricsException(sprintf('Unknown metrics collector given (%s).', $type)); } } - - private static function getHttpClient(): HttpClientInterface - { - return self::$httpClient ??= HttpClient::create(); - } } diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 7f2ada3..7b3ca6c 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -77,12 +77,6 @@ private function createCollector(string $type, array $config): ChildDefinition case 'influxdb': $definition->replaceArgument(0, new Reference($config['influxdb_client'])); - return $definition; - case 'librato': - $definition->replaceArgument(1, $config['host']); - $definition->replaceArgument(2, $config['username']); - $definition->replaceArgument(3, $config['password']); - return $definition; case 'logger': case 'null': diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index 239ab05..919c68f 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -49,18 +49,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->prototype('scalar')->end() ->end() ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['host'])) - ->thenInvalid('The host has to be specified to use a Librato') - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['username'])) - ->thenInvalid('The username has to be specified to use a Librato') - ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'librato' === $v['type'] && empty($v['password'])) - ->thenInvalid('The password has to be specified to use a Librato') - ->end() ->validate() ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index 0d53ed4..71b7e5d 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -5,11 +5,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - - @@ -22,12 +17,6 @@ - - - - - - diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index 607e16e..109f812 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -6,7 +6,6 @@ use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; use Beberlei\Metrics\Collector\InfluxDB; -use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; use Beberlei\Metrics\Collector\Prometheus; @@ -33,7 +32,6 @@ public function getCreateValidMetricTests(): iterable yield [DogStatsD::class, 'dogstatsd', ['host' => 'localhost']]; yield [Graphite::class, 'graphite']; yield [Graphite::class, 'graphite', ['host' => 'localhost', 'port' => 1234]]; - yield [Librato::class, 'librato', ['hostname' => 'foobar.com', 'username' => 'username', 'password' => 'password']]; yield [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]]; yield [Logger::class, 'logger', ['logger' => new NullLogger()]]; yield [NullCollector::class, 'null']; @@ -61,9 +59,6 @@ public function getCreateThrowExceptionIfOptionsAreInvalidTests(): iterable yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['port' => '1234', 'prefix' => 'prefix']]; yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']]; yield ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']]; - yield ['Hostname is required for librato collector.', 'librato']; - yield ['No username given for librato collector.', 'librato', ['hostname' => 'foobar.com']]; - yield ['No password given for librato collector.', 'librato', ['hostname' => 'foobar.com', 'username' => 'username']]; yield ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal']; yield ["Missing 'logger' key with logger service.", 'logger']; yield ["Missing 'client' key for InfluxDB collector.", 'influxdb']; diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index 554730d..f4989d4 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -18,7 +18,6 @@ use Beberlei\Metrics\Collector\Graphite; use Beberlei\Metrics\Collector\InfluxDB; use Beberlei\Metrics\Collector\InMemory; -use Beberlei\Metrics\Collector\Librato; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; use Beberlei\Metrics\Collector\Prometheus; @@ -51,29 +50,6 @@ public function testWithGraphite(): void $this->assertSame(1234, $this->getProperty($collector, 'port')); } - /** - * The source has to be specified to use a Librato. - */ - public function testWithLibratoAndInvalidConfiguration(): void - { - $this->expectException(InvalidConfigurationException::class); - - $container = $this->createContainer(['collectors' => ['simple' => ['type' => 'librato']]], ['beberlei_metrics.collector.librato']); - - $this->assertInstanceOf(Librato::class, $container->get('beberlei_metrics.collector.librato')); - } - - public function testWithLibrato(): void - { - $container = $this->createContainer(['collectors' => ['full' => ['type' => 'librato', 'source' => 'foo.beberlei.de', 'username' => 'foo', 'password' => 'bar']]], ['beberlei_metrics.collector.full']); - - $collector = $container->get('beberlei_metrics.collector.full'); - $this->assertInstanceOf(Librato::class, $collector); - $this->assertSame('foo.beberlei.de', $this->getProperty($collector, 'source')); - $this->assertSame('foo', $this->getProperty($collector, 'username')); - $this->assertSame('bar', $this->getProperty($collector, 'password')); - } - public function testWithLogger(): void { $container = $this->createContainer(['collectors' => ['logger' => ['type' => 'logger']]], ['beberlei_metrics.collector.logger']); From c4b8b61328d46c2abae49ffc20015c68d3dcd71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 23:57:25 +0100 Subject: [PATCH 21/32] chore: fixed license --- .php-cs-fixer.php | 15 +++++++++++++++ LICENSE.md | 19 +++++++++++++++++++ examples/castor.php | 7 +++++++ examples/config/bundles.php | 7 +++++++ examples/config/preload.php | 7 +++++++ examples/importmap.php | 14 +++++--------- examples/public/index.php | 7 +++++++ .../src/Controller/HomepageController.php | 7 +++++++ examples/src/Kernel.php | 7 +++++++ examples/tests/bootstrap.php | 7 +++++++ src/Metrics/Collector/CollectorInterface.php | 14 +++++--------- src/Metrics/Collector/DoctrineDBAL.php | 14 +++++--------- src/Metrics/Collector/DogStatsD.php | 14 +++++--------- .../Collector/GaugeableCollectorInterface.php | 14 +++++--------- src/Metrics/Collector/Graphite.php | 14 +++++--------- src/Metrics/Collector/InMemory.php | 14 +++++--------- src/Metrics/Collector/InfluxDB.php | 14 +++++--------- src/Metrics/Collector/Logger.php | 14 +++++--------- src/Metrics/Collector/NullCollector.php | 14 +++++--------- src/Metrics/Collector/Prometheus.php | 14 +++++--------- src/Metrics/Collector/StatsD.php | 14 +++++--------- .../Collector/TaggableCollectorInterface.php | 14 +++++--------- src/Metrics/Collector/Telegraf.php | 14 +++++--------- src/Metrics/Factory.php | 14 +++++--------- src/Metrics/MetricsException.php | 14 +++++--------- src/Metrics/Utils/Box.php | 7 +++++++ src/MetricsBundle/BeberleiMetricsBundle.php | 14 +++++--------- .../BeberleiMetricsExtension.php | 7 +++++++ .../DependencyInjection/Configuration.php | 7 +++++++ tests/Metrics/Collector/InMemoryTest.php | 14 +++++--------- tests/Metrics/Collector/InfluxDBTest.php | 14 +++++--------- tests/Metrics/Collector/PrometheusTest.php | 14 +++++--------- tests/Metrics/FactoryTest.php | 7 +++++++ .../BeberleiMetricsExtensionTest.php | 14 +++++--------- 34 files changed, 216 insertions(+), 189 deletions(-) create mode 100644 LICENSE.md diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 312ddf8..e7dbb96 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -1,5 +1,19 @@ ignoreVCSIgnored(true) ->in(__DIR__) @@ -16,6 +30,7 @@ '@Symfony' => true, '@Symfony:risky' => true, 'heredoc_indentation' => false, + 'header_comment' => ['header' => $fileHeaderComment], 'single_line_empty_body' => false, 'ordered_types' => false, // From @PhpCsFixer but we don't want it 'php_unit_internal_class' => false, // From @PhpCsFixer but we don't want it diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a96b463 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2004-preset Benjamin Eberlei + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/examples/castor.php b/examples/castor.php index a07f644..dbe4049 100644 --- a/examples/castor.php +++ b/examples/castor.php @@ -1,5 +1,12 @@ ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], diff --git a/examples/config/preload.php b/examples/config/preload.php index db37723..f12aeeb 100644 --- a/examples/config/preload.php +++ b/examples/config/preload.php @@ -1,5 +1,12 @@ [ 'path' => './assets/app.js', diff --git a/examples/public/index.php b/examples/public/index.php index 7fbc8cf..52e8536 100644 --- a/examples/public/index.php +++ b/examples/public/index.php @@ -1,5 +1,12 @@ Date: Fri, 1 Mar 2024 07:11:25 +0100 Subject: [PATCH 22/32] collector: fix doctrine dbal deprecations --- src/Metrics/Collector/DoctrineDBAL.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Metrics/Collector/DoctrineDBAL.php b/src/Metrics/Collector/DoctrineDBAL.php index b8f6046..b3088dc 100644 --- a/src/Metrics/Collector/DoctrineDBAL.php +++ b/src/Metrics/Collector/DoctrineDBAL.php @@ -10,6 +10,7 @@ namespace Beberlei\Metrics\Collector; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; /** * Sends statistics to a relational database. @@ -29,28 +30,28 @@ class DoctrineDBAL implements CollectorInterface private array $data = []; public function __construct( - private readonly Connection $conn + private readonly Connection $conn, ) { } public function measure(string $variable, int $value, array $tags = []): void { - $this->data[] = [$variable, $value, date('Y-m-d')]; + $this->data[] = [$variable, $value, date('Y-m-d H:i:s')]; } public function increment(string $variable, array $tags = []): void { - $this->data[] = [$variable, 1, date('Y-m-d')]; + $this->data[] = [$variable, 1, date('Y-m-d H:i:s')]; } public function decrement(string $variable, array $tags = []): void { - $this->data[] = [$variable, -1, date('Y-m-d')]; + $this->data[] = [$variable, -1, date('Y-m-d H:i:s')]; } public function timing(string $variable, int $time, array $tags = []): void { - $this->data[] = [$variable, $time, date('Y-m-d')]; + $this->data[] = [$variable, $time, date('Y-m-d H:i:s')]; } public function flush(): void @@ -65,9 +66,9 @@ public function flush(): void $stmt = $this->conn->prepare('INSERT INTO metrics (metric, measurement, created) VALUES (?, ?, ?)'); foreach ($this->data as $measurement) { - $stmt->bindParam(1, $measurement[0]); - $stmt->bindParam(2, $measurement[1]); - $stmt->bindParam(3, $measurement[2]); + $stmt->bindValue(1, $measurement[0]); + $stmt->bindValue(2, $measurement[1], ParameterType::INTEGER); + $stmt->bindValue(3, $measurement[2], ParameterType::STRING); $stmt->executeStatement(); } From 9b2b101e7bb75bc97bd2d79762bc029856b7ccd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 12:15:13 +0100 Subject: [PATCH 23/32] collector: change inner dependency of InfluxDB --- composer.json | 3 +- src/Metrics/Collector/InfluxDB.php | 22 +++- src/Metrics/Factory.php | 10 +- .../BeberleiMetricsExtension.php | 29 ++++- .../DependencyInjection/Configuration.php | 30 +++-- .../Resources/config/metrics.xml | 6 +- tests/Metrics/Collector/InfluxDBTest.php | 120 +++++++++++++----- tests/Metrics/FactoryTest.php | 10 +- .../BeberleiMetricsExtensionTest.php | 18 +-- 9 files changed, 162 insertions(+), 86 deletions(-) diff --git a/composer.json b/composer.json index 7646e0a..ac277fc 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,8 @@ }, "require-dev": { "php": ">=8.1", - "corley/influxdb-sdk": "^0.5.1", "doctrine/dbal": "^2.0", + "influxdb/influxdb-php": "^1.15", "jimdo/prometheus_client_php": "^0.5.1", "symfony/config": "^5.4 || ^6.4 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", @@ -48,7 +48,6 @@ } }, "suggest": { - "corley/influxdb-sdk": "For InfluxDB integration", "jimdo/prometheus_client_php": "For Prometheus integration" }, "config": { diff --git a/src/Metrics/Collector/InfluxDB.php b/src/Metrics/Collector/InfluxDB.php index 19e8590..6c3935b 100644 --- a/src/Metrics/Collector/InfluxDB.php +++ b/src/Metrics/Collector/InfluxDB.php @@ -9,14 +9,16 @@ namespace Beberlei\Metrics\Collector; -use InfluxDB\Client; +use InfluxDB\Database; +use InfluxDB\Exception; +use InfluxDB\Point; class InfluxDB implements CollectorInterface, TaggableCollectorInterface { private array $data = []; public function __construct( - private readonly Client $client, + private readonly Database $database, private array $tags = [], ) { } @@ -43,12 +45,18 @@ public function timing(string $variable, int $time, array $tags = []): void public function flush(): void { + $points = []; foreach ($this->data as $data) { - try { - $this->client->mark(['points' => [['measurement' => $data[0], 'fields' => ['value' => $data[1]]]], 'tags' => $data[2] + $this->tags]); - } catch (\Exception) { - continue; - } + $points[] = new Point( + $data[0], + $data[1], + $this->tags + $data[2], + ); + } + + try { + $this->database->writePoints($points, Database::PRECISION_SECONDS); + } catch (Exception) { } $this->data = []; diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index f56c48b..349dbc6 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -115,24 +115,24 @@ public static function create(string $type, array $options = []): CollectorInter case 'logger': if (!isset($options['logger'])) { - throw new MetricsException("Missing 'logger' key with logger service."); + throw new MetricsException('Missing "logger" key with logger service.'); } return new Logger($options['logger']); case 'influxdb': - if (!isset($options['client'])) { - throw new MetricsException("Missing 'client' key for InfluxDB collector."); + if (!isset($options['database'])) { + throw new MetricsException('Missing "database" key for InfluxDB collector.'); } - return new InfluxDB($options['client']); + return new InfluxDB($options['database']); case 'null': return new NullCollector(); case 'prometheus': if (!isset($options['collector_registry'])) { - throw new MetricsException("Missing 'collector_registry' key for Prometheus collector."); + throw new MetricsException('Missing "collector_registry" key for Prometheus collector.'); } $namespace = $options['namespace'] ?? ''; diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 3b228c5..080ee4a 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -10,7 +10,6 @@ namespace Beberlei\Bundle\MetricsBundle\DependencyInjection; use Beberlei\Metrics\Collector\CollectorInterface; -use Prometheus\Collector; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -64,6 +63,7 @@ private function createCollector(string $type, array $config): ChildDefinition $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'kernel.terminate']); $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'console.terminate']); $definition->addTag(CollectorInterface::class); + // $definition->addTag('kernel.reset'); if ($config['tags'] ?? []) { $definition->addMethodCall('setTags', [$config['tags']]); @@ -76,13 +76,28 @@ private function createCollector(string $type, array $config): ChildDefinition return $definition; case 'graphite': - $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(0, $config['host']); $definition->replaceArgument(1, $config['port'] ?? 2003); $definition->replaceArgument(2, $config['protocol'] ?? 'tcp'); return $definition; case 'influxdb': - $definition->replaceArgument(0, new Reference($config['influxdb_client'])); + if (!class_exists(\InfluxDB\Client::class)) { + throw new \LogicException('The "influxdb/influxdb-php" package is required to use the "influxdb" collector.'); + } + if ($config['service']) { + $definition->replaceArgument(0, new Reference($config['service'])); + } else { + $database = new ChildDefinition('beberlei_metrics.collector_proto.influxdb.database'); + $database->replaceArgument(0, sprintf('influxdb://%s:%s@%s:%s/%s', + $config['username'], + $config['password'], + $config['host'], + $config['port'] ?? 8086, + $config['database'], + )); + $definition->replaceArgument(0, $database); + } return $definition; case 'logger': @@ -96,15 +111,15 @@ private function createCollector(string $type, array $config): ChildDefinition return $definition; case 'statsd': case 'dogstatsd': - $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(0, $config['host']); $definition->replaceArgument(1, $config['port'] ?? 8125); - $definition->replaceArgument(2, (string) $config['prefix']); + $definition->replaceArgument(2, $config['prefix']); return $definition; case 'telegraf': - $definition->replaceArgument(0, $config['host'] ?? 'localhost'); + $definition->replaceArgument(0, $config['host']); $definition->replaceArgument(1, $config['port'] ?? 8125); - $definition->replaceArgument(2, (string) $config['prefix']); + $definition->replaceArgument(2, $config['prefix']); return $definition; default: diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index f8fb443..1246ed3 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -39,26 +39,32 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() - ->scalarNode('connection')->defaultNull()->end() - ->scalarNode('file')->defaultNull()->end() - ->scalarNode('host')->defaultNull()->end() - ->scalarNode('password')->defaultNull()->end() - ->integerNode('port')->defaultNull()->end() - ->scalarNode('prefix')->defaultNull()->end() + ->scalarNode('host')->defaultValue('localhost')->end() ->scalarNode('protocol')->defaultNull()->end() - ->scalarNode('source')->defaultNull()->end() - ->scalarNode('username')->defaultNull()->end() - ->scalarNode('influxdb_client')->defaultNull()->end() - ->scalarNode('prometheus_collector_registry')->defaultNull()->info('It must to contain service id for Prometheus\\CollectorRegistry class instance.')->end() - ->scalarNode('namespace')->defaultValue('')->end() + ->integerNode('port')->defaultNull()->end() + ->scalarNode('username')->defaultValue('')->end() + ->scalarNode('password')->defaultValue('')->end() + ->scalarNode('prefix')->defaultValue('')->end() + ->scalarNode('service')->defaultNull()->end() ->arrayNode('tags') ->defaultValue([]) ->prototype('scalar')->end() ->end() + // Doctrine DBAL stuff + ->scalarNode('connection')->defaultNull()->end() + // Prom stuff + ->scalarNode('prometheus_collector_registry')->defaultNull()->info('It must to contain service id for Prometheus\\CollectorRegistry class instance.')->end() + ->scalarNode('namespace')->defaultValue('')->end() + // InfluxDB stuff + ->scalarNode('database')->defaultValue('')->end() ->end() ->validate() ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) - ->thenInvalid('The prometheus_collector_registry has to be specified to use a Prometheus') + ->thenInvalid('The "prometheus_collector_registry" has to be specified to use a Prometheus') + ->end() + ->validate() + ->ifTrue(static fn ($v): bool => 'influxdb' === $v['type'] && empty($v['database'])) + ->thenInvalid('The "database" has to be specified to use a InfluxDB') ->end() ->end() ->end() diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index 71b7e5d..bd54cc8 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -14,8 +14,12 @@ + + + + - + diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDBTest.php index be44fc6..d026dd4 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDBTest.php @@ -10,34 +10,31 @@ namespace Beberlei\Metrics\Tests\Collector; use Beberlei\Metrics\Collector\InfluxDB; -use InfluxDB\Client; +use InfluxDB\Database; +use InfluxDB\Point; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class InfluxDBTest extends TestCase { - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $client; + private MockObject&Database $database; private InfluxDB $collector; protected function setUp(): void { - $this->client = $this->getMockBuilder(Client::class) + $this->database = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->getMock() ; - $this->collector = new InfluxDB($this->client); + $this->collector = new InfluxDB($this->database); } public function testCollectIncrement(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 1]]], 'tags' => []]; - - $this->client->expects($this->once()) - ->method('mark') - ->with($expectedArgs) + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->isType('array')) ; $this->collector->increment('series-name'); @@ -46,11 +43,20 @@ public function testCollectIncrement(): void public function testCollectDecrement(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => -1]]], 'tags' => []]; - - $this->client->expects($this->once()) - ->method('mark') - ->with($expectedArgs) + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->callback(function ($arg0) { + $this->assertIsArray($arg0); + $this->assertCount(1, $arg0); + $this->assertArrayHasKey(0, $arg0); + $point = $arg0[0]; + $this->assertInstanceOf(Point::class, $point); + $this->assertSame('series-name', $point->getMeasurement()); + $this->assertSame(['value' => '-1i'], $point->getFields()); + $this->assertSame([], $point->getTags()); + + return true; + })) ; $this->collector->decrement('series-name'); @@ -59,11 +65,20 @@ public function testCollectDecrement(): void public function testCollectTiming(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => []]; - - $this->client->expects($this->once()) - ->method('mark') - ->with($expectedArgs) + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->callback(function ($arg0) { + $this->assertIsArray($arg0); + $this->assertCount(1, $arg0); + $this->assertArrayHasKey(0, $arg0); + $point = $arg0[0]; + $this->assertInstanceOf(Point::class, $point); + $this->assertSame('series-name', $point->getMeasurement()); + $this->assertSame(['value' => '47i'], $point->getFields()); + $this->assertSame([], $point->getTags()); + + return true; + })) ; $this->collector->timing('series-name', 47); @@ -72,11 +87,20 @@ public function testCollectTiming(): void public function testCollectMeasure(): void { - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => []]; - - $this->client->expects($this->once()) - ->method('mark') - ->with($expectedArgs) + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->callback(function ($arg0) { + $this->assertIsArray($arg0); + $this->assertCount(1, $arg0); + $this->assertArrayHasKey(0, $arg0); + $point = $arg0[0]; + $this->assertInstanceOf(Point::class, $point); + $this->assertSame('series-name', $point->getMeasurement()); + $this->assertSame(['value' => '47i'], $point->getFields()); + $this->assertSame([], $point->getTags()); + + return true; + })) ; $this->collector->measure('series-name', 47); @@ -87,15 +111,47 @@ public function testCollectMeasureWithTags(): void { $expectedTags = ['dc' => 'west', 'node' => 'nemesis101']; - $expectedArgs = ['points' => [['measurement' => 'series-name', 'fields' => ['value' => 47]]], 'tags' => $expectedTags]; - - $this->client->expects($this->once()) - ->method('mark') - ->with($expectedArgs) + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->callback(function ($arg0) use ($expectedTags) { + $this->assertIsArray($arg0); + $this->assertCount(1, $arg0); + $this->assertArrayHasKey(0, $arg0); + $point = $arg0[0]; + $this->assertInstanceOf(Point::class, $point); + $this->assertSame('series-name', $point->getMeasurement()); + $this->assertSame(['value' => '47i'], $point->getFields()); + $this->assertSame($expectedTags, $point->getTags()); + + return true; + })) ; $this->collector->setTags($expectedTags); $this->collector->measure('series-name', 47); $this->collector->flush(); } + + public function testCollectMeasureWithTagsMerged(): void + { + $this->database->expects($this->once()) + ->method('writePoints') + ->with($this->callback(function ($arg0) { + $this->assertIsArray($arg0); + $this->assertCount(1, $arg0); + $this->assertArrayHasKey(0, $arg0); + $point = $arg0[0]; + $this->assertInstanceOf(Point::class, $point); + $this->assertSame('series-name', $point->getMeasurement()); + $this->assertSame(['value' => '47i'], $point->getFields()); + $this->assertSame(['dc' => 'west', 'node' => 'nemesis101', 'foo' => 'bar'], $point->getTags()); + + return true; + })) + ; + + $collector = new InfluxDB($this->database, ['dc' => 'west', 'node' => 'nemesis101']); + $collector->measure('series-name', 47, ['foo' => 'bar']); + $collector->flush(); + } } diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index 123455e..e6d98b2 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -20,7 +20,7 @@ use Beberlei\Metrics\Factory; use Beberlei\Metrics\MetricsException; use Doctrine\DBAL\Connection; -use InfluxDB\Client; +use InfluxDB\Database; use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; use Psr\Log\NullLogger; @@ -42,7 +42,7 @@ public function getCreateValidMetricTests(): iterable yield [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]]; yield [Logger::class, 'logger', ['logger' => new NullLogger()]]; yield [NullCollector::class, 'null']; - yield [InfluxDB::class, 'influxdb', ['client' => $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock()]]; + yield [InfluxDB::class, 'influxdb', ['database' => $this->getMockBuilder(Database::class)->disableOriginalConstructor()->getMock()]]; yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock()]]; yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace']]; } @@ -67,9 +67,9 @@ public function getCreateThrowExceptionIfOptionsAreInvalidTests(): iterable yield ['You should specified a host and a port if you specified a prefix.', 'dogstatsd', ['hostname' => 'foobar.com', 'prefix' => 'prefix']]; yield ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']]; yield ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal']; - yield ["Missing 'logger' key with logger service.", 'logger']; - yield ["Missing 'client' key for InfluxDB collector.", 'influxdb']; - yield ["Missing 'collector_registry' key for Prometheus collector.", 'prometheus']; + yield ['Missing "logger" key with logger service.', 'logger']; + yield ['Missing "database" key for InfluxDB collector.', 'influxdb']; + yield ['Missing "collector_registry" key for Prometheus collector.', 'prometheus']; } /** diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index 7080858..c698eda 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -19,7 +19,6 @@ use Beberlei\Metrics\Collector\Prometheus; use Beberlei\Metrics\Collector\StatsD; use Beberlei\Metrics\Collector\Telegraf; -use InfluxDB\Client; use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; use Psr\Log\NullLogger; @@ -117,28 +116,17 @@ public function testWithTelegraf(): void public function testWithInfluxDB(): void { - $influxDBClientMock = $this->getMockBuilder(Client::class) - ->disableOriginalConstructor() - ->getMock() - ; - - $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'influxdb_client' => 'influxdb_client_mock']]], ['beberlei_metrics.collector.influxdb'], ['influxdb_client_mock' => $influxDBClientMock]); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'database' => 'foobar']]], ['beberlei_metrics.collector.influxdb']); $collector = $container->get('beberlei_metrics.collector.influxdb'); $this->assertInstanceOf(InfluxDB::class, $collector); - $this->assertSame($influxDBClientMock, $this->getProperty($collector, 'client')); } public function testWithInfluxDBAndWithTags(): void { $expectedTags = ['string_tag' => 'first_value', 'int_tag' => 123]; - $influxDBClientMock = $this->getMockBuilder(Client::class) - ->disableOriginalConstructor() - ->getMock() - ; - - $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'influxdb_client' => 'influxdb_client_mock', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.influxdb'], ['influxdb_client_mock' => $influxDBClientMock]); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'database' => 'foobar', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.influxdb']); $collector = $container->get('beberlei_metrics.collector.influxdb'); $this->assertInstanceOf(InfluxDB::class, $collector); @@ -203,7 +191,7 @@ public function testWithPrometheusAndWithTags(): void public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified(): void { $this->expectException(InvalidConfigurationException::class); - $this->expectExceptionMessage('The prometheus_collector_registry has to be specified to use a Prometheus'); + $this->expectExceptionMessage('The "prometheus_collector_registry" has to be specified to use a Prometheus'); $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]]); } From ceae017961eb0dae559335e5b19e1e88f901de3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 14:14:45 +0100 Subject: [PATCH 24/32] collector: rename InfluxDB to InfluxDbV1 --- README.md | 2 +- composer.json | 2 ++ src/Metrics/Collector/{InfluxDB.php => InfluxDbV1.php} | 2 +- src/Metrics/Factory.php | 6 +++--- .../DependencyInjection/BeberleiMetricsExtension.php | 4 ++-- src/MetricsBundle/Resources/config/metrics.xml | 4 ++-- .../Collector/{InfluxDBTest.php => InfluxDbV1Test.php} | 10 +++++----- tests/Metrics/FactoryTest.php | 6 +++--- .../BeberleiMetricsExtensionTest.php | 10 +++++----- 9 files changed, 24 insertions(+), 22 deletions(-) rename src/Metrics/Collector/{InfluxDB.php => InfluxDbV1.php} (95%) rename tests/Metrics/Collector/{InfluxDBTest.php => InfluxDbV1Test.php} (94%) diff --git a/README.md b/README.md index f160c04..ad0fd99 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ beberlei_metrics: type: monolog influxdb: type: influxdb - influxdb_client: influxdb_client_service # using the InfluxDB client service named "influxdb_client_service" + database: metrics tags: dc: "west" node_instance: "hermes10" diff --git a/composer.json b/composer.json index ac277fc..e8b17cf 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ }, "conflict": { "doctrine/dbal": "<2", + "influxdb/influxdb-php": "<1.15", "jimdo/prometheus_client_php": "<0.5.1", "symfony/config": "<5.4", "symfony/dependency-injection": "<5.4", @@ -48,6 +49,7 @@ } }, "suggest": { + "influxdb/influxdb-php": "For InfluxDB integration", "jimdo/prometheus_client_php": "For Prometheus integration" }, "config": { diff --git a/src/Metrics/Collector/InfluxDB.php b/src/Metrics/Collector/InfluxDbV1.php similarity index 95% rename from src/Metrics/Collector/InfluxDB.php rename to src/Metrics/Collector/InfluxDbV1.php index 6c3935b..3d8bde3 100644 --- a/src/Metrics/Collector/InfluxDB.php +++ b/src/Metrics/Collector/InfluxDbV1.php @@ -13,7 +13,7 @@ use InfluxDB\Exception; use InfluxDB\Point; -class InfluxDB implements CollectorInterface, TaggableCollectorInterface +class InfluxDbV1 implements CollectorInterface, TaggableCollectorInterface { private array $data = []; diff --git a/src/Metrics/Factory.php b/src/Metrics/Factory.php index 349dbc6..32680a8 100644 --- a/src/Metrics/Factory.php +++ b/src/Metrics/Factory.php @@ -13,7 +13,7 @@ use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; -use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InfluxDbV1; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; use Beberlei\Metrics\Collector\Prometheus; @@ -120,12 +120,12 @@ public static function create(string $type, array $options = []): CollectorInter return new Logger($options['logger']); - case 'influxdb': + case 'influxdb_v1': if (!isset($options['database'])) { throw new MetricsException('Missing "database" key for InfluxDB collector.'); } - return new InfluxDB($options['database']); + return new InfluxDbV1($options['database']); case 'null': return new NullCollector(); diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 080ee4a..5620df7 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -81,14 +81,14 @@ private function createCollector(string $type, array $config): ChildDefinition $definition->replaceArgument(2, $config['protocol'] ?? 'tcp'); return $definition; - case 'influxdb': + case 'influxdb_v1': if (!class_exists(\InfluxDB\Client::class)) { throw new \LogicException('The "influxdb/influxdb-php" package is required to use the "influxdb" collector.'); } if ($config['service']) { $definition->replaceArgument(0, new Reference($config['service'])); } else { - $database = new ChildDefinition('beberlei_metrics.collector_proto.influxdb.database'); + $database = new ChildDefinition('beberlei_metrics.collector_proto.influxdb_v1.database'); $database->replaceArgument(0, sprintf('influxdb://%s:%s@%s:%s/%s', $config['username'], $config['password'], diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index bd54cc8..e0bcb14 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -14,11 +14,11 @@ - + - + diff --git a/tests/Metrics/Collector/InfluxDBTest.php b/tests/Metrics/Collector/InfluxDbV1Test.php similarity index 94% rename from tests/Metrics/Collector/InfluxDBTest.php rename to tests/Metrics/Collector/InfluxDbV1Test.php index d026dd4..e072c2f 100644 --- a/tests/Metrics/Collector/InfluxDBTest.php +++ b/tests/Metrics/Collector/InfluxDbV1Test.php @@ -9,17 +9,17 @@ namespace Beberlei\Metrics\Tests\Collector; -use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InfluxDbV1; use InfluxDB\Database; use InfluxDB\Point; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -class InfluxDBTest extends TestCase +class InfluxDbV1Test extends TestCase { private MockObject&Database $database; - private InfluxDB $collector; + private InfluxDbV1 $collector; protected function setUp(): void { @@ -27,7 +27,7 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMock() ; - $this->collector = new InfluxDB($this->database); + $this->collector = new InfluxDbV1($this->database); } public function testCollectIncrement(): void @@ -150,7 +150,7 @@ public function testCollectMeasureWithTagsMerged(): void })) ; - $collector = new InfluxDB($this->database, ['dc' => 'west', 'node' => 'nemesis101']); + $collector = new InfluxDbV1($this->database, ['dc' => 'west', 'node' => 'nemesis101']); $collector->measure('series-name', 47, ['foo' => 'bar']); $collector->flush(); } diff --git a/tests/Metrics/FactoryTest.php b/tests/Metrics/FactoryTest.php index e6d98b2..6c08fc4 100644 --- a/tests/Metrics/FactoryTest.php +++ b/tests/Metrics/FactoryTest.php @@ -12,7 +12,7 @@ use Beberlei\Metrics\Collector\DoctrineDBAL; use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; -use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InfluxDbV1; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; use Beberlei\Metrics\Collector\Prometheus; @@ -42,7 +42,7 @@ public function getCreateValidMetricTests(): iterable yield [DoctrineDBAL::class, 'doctrine_dbal', ['connection' => $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock()]]; yield [Logger::class, 'logger', ['logger' => new NullLogger()]]; yield [NullCollector::class, 'null']; - yield [InfluxDB::class, 'influxdb', ['database' => $this->getMockBuilder(Database::class)->disableOriginalConstructor()->getMock()]]; + yield [InfluxDbV1::class, 'influxdb_v1', ['database' => $this->getMockBuilder(Database::class)->disableOriginalConstructor()->getMock()]]; yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock()]]; yield [Prometheus::class, 'prometheus', ['collector_registry' => $this->getMockBuilder(CollectorRegistry::class)->disableOriginalConstructor()->getMock(), 'namespace' => 'some_namespace']]; } @@ -68,7 +68,7 @@ public function getCreateThrowExceptionIfOptionsAreInvalidTests(): iterable yield ['You should specified a host if you specified a port.', 'graphite', ['port' => '1234']]; yield ['connection is required for Doctrine DBAL collector.', 'doctrine_dbal']; yield ['Missing "logger" key with logger service.', 'logger']; - yield ['Missing "database" key for InfluxDB collector.', 'influxdb']; + yield ['Missing "database" key for InfluxDB collector.', 'influxdb_v1']; yield ['Missing "collector_registry" key for Prometheus collector.', 'prometheus']; } diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index c698eda..c0e8115 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -12,7 +12,7 @@ use Beberlei\Bundle\MetricsBundle\DependencyInjection\BeberleiMetricsExtension; use Beberlei\Metrics\Collector\DogStatsD; use Beberlei\Metrics\Collector\Graphite; -use Beberlei\Metrics\Collector\InfluxDB; +use Beberlei\Metrics\Collector\InfluxDbV1; use Beberlei\Metrics\Collector\InMemory; use Beberlei\Metrics\Collector\Logger; use Beberlei\Metrics\Collector\NullCollector; @@ -116,20 +116,20 @@ public function testWithTelegraf(): void public function testWithInfluxDB(): void { - $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'database' => 'foobar']]], ['beberlei_metrics.collector.influxdb']); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb_v1', 'database' => 'foobar']]], ['beberlei_metrics.collector.influxdb']); $collector = $container->get('beberlei_metrics.collector.influxdb'); - $this->assertInstanceOf(InfluxDB::class, $collector); + $this->assertInstanceOf(InfluxDbV1::class, $collector); } public function testWithInfluxDBAndWithTags(): void { $expectedTags = ['string_tag' => 'first_value', 'int_tag' => 123]; - $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb', 'database' => 'foobar', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.influxdb']); + $container = $this->createContainer(['collectors' => ['influxdb' => ['type' => 'influxdb_v1', 'database' => 'foobar', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.influxdb']); $collector = $container->get('beberlei_metrics.collector.influxdb'); - $this->assertInstanceOf(InfluxDB::class, $collector); + $this->assertInstanceOf(InfluxDbV1::class, $collector); $this->assertEquals($expectedTags, $this->getProperty($collector, 'tags')); } From a6bf9abcfef0c82a5e39bd4772ed7ef1418ecbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 14:24:53 +0100 Subject: [PATCH 25/32] bundle: enhance configuration + add kernel.reset tag --- .../BeberleiMetricsExtension.php | 15 ++++++++++++++- .../DependencyInjection/Configuration.php | 9 +++------ .../BeberleiMetricsExtensionTest.php | 7 +++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 5620df7..0c9815d 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -20,6 +20,19 @@ class BeberleiMetricsExtension extends Extension { + public const TYPES = [ + 'doctrine_dbal', + 'dogstatsd', + 'graphite', + 'influxdb_v1', + 'logger', + 'memory', + 'null', + 'prometheus', + 'statsd', + 'telegraf', + ]; + public function load(array $configs, ContainerBuilder $container): void { $configuration = $this->getConfiguration($configs, $container) ?? throw new \LogicException('Expected configuration to be set'); @@ -63,7 +76,7 @@ private function createCollector(string $type, array $config): ChildDefinition $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'kernel.terminate']); $definition->addTag('kernel.event_listener', ['method' => 'flush', 'priority' => -1024, 'event' => 'console.terminate']); $definition->addTag(CollectorInterface::class); - // $definition->addTag('kernel.reset'); + $definition->addTag('kernel.reset', ['method' => 'flush']); if ($config['tags'] ?? []) { $definition->addMethodCall('setTags', [$config['tags']]); diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index 1246ed3..f7a36e6 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -31,13 +31,10 @@ public function getConfigTreeBuilder(): TreeBuilder ->useAttributeAsKey('name') ->prototype('array') ->children() - ->scalarNode('type') + ->enumNode('type') + ->values(BeberleiMetricsExtension::TYPES) ->isRequired() - ->validate() - ->ifTrue(static fn ($v) => !\is_string($v)) - ->thenInvalid('The type must be a string got "%s".') - ->end() - + ->cannotBeEmpty() ->end() ->scalarNode('host')->defaultValue('localhost')->end() ->scalarNode('protocol')->defaultNull()->end() diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index c0e8115..4374562 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -195,6 +195,13 @@ public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistry $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]]); } + public function testWithInvalid(): void + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessageMatches('/^The value "invalid" is not allowed for path "beberlei_metrics.collectors.invalid.type". Permissible values: /'); + $this->createContainer(['collectors' => ['invalid' => ['type' => 'invalid']]]); + } + private function createContainer($configs, array $publicServices = [], array $additionalServices = []): ContainerBuilder { $container = new ContainerBuilder(); From e0c3d027dd8e410389d047fb7757097785ed2224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 16:50:14 +0100 Subject: [PATCH 26/32] collector: change inner dependency of Prometheus --- src/Metrics/Collector/Prometheus.php | 76 +++-- .../BeberleiMetricsExtension.php | 25 +- .../DependencyInjection/Configuration.php | 4 - .../Resources/config/metrics.xml | 5 + tests/Metrics/Collector/PrometheusTest.php | 292 +++++------------- .../BeberleiMetricsExtensionTest.php | 24 +- 6 files changed, 143 insertions(+), 283 deletions(-) diff --git a/src/Metrics/Collector/Prometheus.php b/src/Metrics/Collector/Prometheus.php index 9be13d3..a2bf557 100644 --- a/src/Metrics/Collector/Prometheus.php +++ b/src/Metrics/Collector/Prometheus.php @@ -10,15 +10,14 @@ namespace Beberlei\Metrics\Collector; use Prometheus\CollectorRegistry; -use Prometheus\Exception\MetricNotFoundException; -use Prometheus\Gauge; -class Prometheus implements CollectorInterface, TaggableCollectorInterface +class Prometheus implements CollectorInterface, GaugeableCollectorInterface, TaggableCollectorInterface { - private array $data = ['counters' => [], 'gauges' => []]; + private array $counters = []; + private array $gauges = []; public function __construct( - private readonly CollectorRegistry $collectorRegistry, + private readonly CollectorRegistry $registry, private readonly string $namespace = '', private array $tags = [], ) { @@ -26,49 +25,55 @@ public function __construct( public function measure(string $variable, int $value, array $tags = []): void { - $this->data['gauges'][] = ['name' => $variable, 'value' => $value]; + $this->gauge($variable, $value, $tags); } public function increment(string $variable, array $tags = []): void { - $this->data['counters'][] = ['name' => $variable, 'value' => 1]; + $this->counters[] = ['variable' => $variable, 'value' => 1, 'tags' => $tags]; } public function decrement(string $variable, array $tags = []): void { - $this->data['counters'][] = ['name' => $variable, 'value' => -1]; + $this->counters[] = ['variable' => $variable, 'value' => -1, 'tags' => $tags]; } public function timing(string $variable, int $time, array $tags = []): void { - $this->measure($variable, $time); + $this->gauge($variable, $time, $tags); } - public function flush(): void + public function gauge(string $variable, string|int $value, array $tags = []): void { - if (!$this->data['gauges'] && !$this->data['counters']) { - return; - } - - $tagsValues = array_values($this->tags); - - foreach ($this->data['counters'] as $counterData) { - $gauge = $this->getOrRegisterGaugeForVariable($counterData['name']); + $this->gauges[] = ['variable' => $variable, 'value' => $value, 'tags' => $tags]; + } - if ($counterData['value'] > 0) { - $gauge->inc($tagsValues); - } elseif ($counterData['value'] < 0) { - $gauge->dec($tagsValues); + public function flush(): void + { + try { + foreach ($this->counters as $counter) { + $variable = $this->normalizeVariable($counter['variable']); + $labels = $this->tags + $counter['tags']; + $this + ->registry + ->getOrRegisterCounter($this->namespace, $variable, '', $labels) + ->incBy($counter['value'], $labels) + ; } - } - foreach ($this->data['gauges'] as $gaugeData) { - $gauge = $this->getOrRegisterGaugeForVariable($gaugeData['name']); - - $gauge->set($gaugeData['value'], $tagsValues); + foreach ($this->gauges as $gauge) { + $variable = $this->normalizeVariable($gauge['variable']); + $labels = $this->tags + $gauge['tags']; + $this + ->registry + ->getOrRegisterGauge($this->namespace, $variable, '', $labels) + ->set($gauge['value'], $labels) + ; + } + } catch (\Exception) { } - $this->data = ['counters' => [], 'gauges' => []]; + $this->counters = $this->gauges = []; } public function setTags(array $tags): void @@ -76,19 +81,8 @@ public function setTags(array $tags): void $this->tags = $tags; } - private function getOrRegisterGaugeForVariable(string $variable): Gauge + private function normalizeVariable(string $variable): string { - try { - $gauge = $this->collectorRegistry->getGauge($this->namespace, $variable); - } catch (MetricNotFoundException) { - $gauge = $this->collectorRegistry->registerGauge( - $this->namespace, - $variable, - '', - array_keys($this->tags) - ); - } - - return $gauge; + return str_replace(['.', ':'], ['_', '_'], $variable); } } diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 0c9815d..1d8bdd5 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -10,6 +10,7 @@ namespace Beberlei\Bundle\MetricsBundle\DependencyInjection; use Beberlei\Metrics\Collector\CollectorInterface; +use Prometheus\CollectorRegistry; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -47,7 +48,7 @@ public function load(array $configs, ContainerBuilder $container): void ]; } foreach ($config['collectors'] as $name => $colConfig) { - $definition = $this->createCollector($colConfig['type'], $colConfig); + $definition = $this->createCollector($container, $name, $colConfig['type'], $colConfig); $container->setDefinition('beberlei_metrics.collector.' . $name, $definition); $container->registerAliasForArgument('beberlei_metrics.collector.' . $name, CollectorInterface::class, $name); } @@ -63,12 +64,10 @@ public function load(array $configs, ContainerBuilder $container): void throw new InvalidArgumentException('No default collector is configured and there is more than one collector. Please define a default collector'); } - if ($name) { - $container->setAlias(CollectorInterface::class, 'beberlei_metrics.collector.' . $name); - } + $container->setAlias(CollectorInterface::class, 'beberlei_metrics.collector.' . $name); } - private function createCollector(string $type, array $config): ChildDefinition + private function createCollector(ContainerBuilder $container, string $name, string $type, array $config): ChildDefinition { $definition = new ChildDefinition('beberlei_metrics.collector_proto.' . $config['type']); @@ -118,7 +117,21 @@ private function createCollector(string $type, array $config): ChildDefinition case 'memory': return $definition; case 'prometheus': - $definition->replaceArgument(0, new Reference($config['prometheus_collector_registry'])); + if (!class_exists(CollectorRegistry::class)) { + throw new \LogicException('The "promphp/prometheus_client_php" package is required to use the "prometheus" collector.'); + } + if ($config['service']) { + $definition->replaceArgument(0, new Reference($config['service'])); + } else { + $database = new ChildDefinition('beberlei_metrics.collector_proto.prometheus.registry'); + $container->setDefinition($id = 'beberlei_metrics.collector.' . $name . '.prometheus.registry', $database); + + $definition->replaceArgument(0, new Reference($id)); + if (!$container->hasAlias(CollectorRegistry::class)) { + $container->setAlias(CollectorRegistry::class, $id); + } + } + $definition->replaceArgument(1, $config['namespace']); return $definition; diff --git a/src/MetricsBundle/DependencyInjection/Configuration.php b/src/MetricsBundle/DependencyInjection/Configuration.php index f7a36e6..21bab64 100644 --- a/src/MetricsBundle/DependencyInjection/Configuration.php +++ b/src/MetricsBundle/DependencyInjection/Configuration.php @@ -55,10 +55,6 @@ public function getConfigTreeBuilder(): TreeBuilder // InfluxDB stuff ->scalarNode('database')->defaultValue('')->end() ->end() - ->validate() - ->ifTrue(static fn ($v): bool => 'prometheus' === $v['type'] && empty($v['prometheus_collector_registry'])) - ->thenInvalid('The "prometheus_collector_registry" has to be specified to use a Prometheus') - ->end() ->validate() ->ifTrue(static fn ($v): bool => 'influxdb' === $v['type'] && empty($v['database'])) ->thenInvalid('The "database" has to be specified to use a InfluxDB') diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index e0bcb14..fd1f01c 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -27,6 +27,11 @@ + + + + + diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index 0dde72b..c8c1393 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -10,271 +10,138 @@ namespace Beberlei\Metrics\Tests\Collector; use Beberlei\Metrics\Collector\Prometheus; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Prometheus\CollectorRegistry; -use Prometheus\Exception\MetricNotFoundException; -use Prometheus\Gauge; +use Prometheus\Storage\InMemory; class PrometheusTest extends TestCase { - public const TEST_NAMESPACE = 'some_metric_namespace'; - - public const TEST_VARIABLE_NAME = 'some_variable_name'; - - private MockObject&CollectorRegistry $collectorRegistryMock; + private const TEST_NAMESPACE = 'some_metric_namespace'; + private const TEST_VARIABLE_NAME = 'some_variable_name'; + private CollectorRegistry $collectorRegistry; private Prometheus $collector; protected function setUp(): void { - $this->collectorRegistryMock = $this->getMockBuilder(CollectorRegistry::class) - ->disableOriginalConstructor() - ->getMock() - ; - - $this->collector = new Prometheus($this->collectorRegistryMock, self::TEST_NAMESPACE); + $this->collectorRegistry = new CollectorRegistry(new InMemory(), false); + $this->collector = new Prometheus($this->collectorRegistry, self::TEST_NAMESPACE); } public function testMeasure(): void { $expectedVariableValue = 123; + $labels = []; - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('set') - ->with($expectedVariableValue, []) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - + $this->collector->setTags($labels); $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $expectedVariableValue, $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testMeasureWithTags(): void { $expectedVariableValue = 123; - $expectedTagsValues = ['value1', 'value2']; - - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('set') - ->with($expectedVariableValue, $expectedTagsValues) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - - $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); + $labels = ['tag1' => 'value1', 'tag2' => 'value2']; + $this->collector->setTags($labels); $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $expectedVariableValue, $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testIncrement(): void { - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('inc') - ->with([]) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; + $labels = []; + $this->collector->setTags($labels); $this->collector->increment(self::TEST_VARIABLE_NAME); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame('1', $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testIncrementWithTags(): void { - $expectedTagsValues = ['value1', 'value2']; - - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('inc') - ->with($expectedTagsValues) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - - $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); + $labels = ['value1', 'value2']; + $this->collector->setTags($labels); $this->collector->increment(self::TEST_VARIABLE_NAME); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame('1', $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testDecrement(): void { - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('dec') - ->with([]) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; + $labels = []; + $this->collector->setTags($labels); $this->collector->decrement(self::TEST_VARIABLE_NAME); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame('-1', $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testDecrementWithTags(): void { - $expectedTagsValues = ['value1', 'value2']; - - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('dec') - ->with($expectedTagsValues) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - - $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); + $labels = ['value1', 'value2']; + $this->collector->setTags($labels); $this->collector->decrement(self::TEST_VARIABLE_NAME); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame('-1', $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testTiming(): void { $expectedVariableValue = 123; + $labels = []; - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('set') - ->with($expectedVariableValue, []) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - + $this->collector->setTags($labels); $this->collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $expectedVariableValue, $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } public function testTimingWithTags(): void { $expectedVariableValue = 123; - $expectedTagsValues = ['value1', 'value2']; - - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('set') - ->with($expectedVariableValue, $expectedTagsValues) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - - $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); + $labels = ['tag1' => 'value1', 'tag2' => 'value2']; + $this->collector->setTags($labels); $this->collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); $this->collector->flush(); - } - - public function testMeasureWhenSetNewVariableWithTags(): void - { - $expectedVariableValue = 123; - $expectedTagsNames = ['tag1', 'tag2']; - $expectedTagsValues = ['value1', 'value2']; - - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($this->once()) - ->method('set') - ->with($expectedVariableValue, $expectedTagsValues) - ; - - $this->collectorRegistryMock - ->expects($this->once()) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willThrowException(new MetricNotFoundException()) - ; - $this->collectorRegistryMock - ->expects($this->once()) - ->method('registerGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME, '', $expectedTagsNames) - ->willReturn($gaugeMock) - ; - - $this->collector->setTags(['tag1' => 'value1', 'tag2' => 'value2']); - $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); - $this->collector->flush(); + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $expectedVariableValue, $data->getValue()); + $this->assertSame($labels, $data->getLabelValues()); } /** @@ -283,34 +150,21 @@ public function testMeasureWhenSetNewVariableWithTags(): void public function testFlushWhenCallsTwiceWithDifferentData(): void { $firstExpectedVariableValue = 123; - $secondExpectedVariableValue = 321; - $gaugeMock = $this->getMockBuilder(Gauge::class) - ->disableOriginalConstructor() - ->getMock() - ; - $gaugeMock - ->expects($matcher = $this->exactly(2)) - ->method('set') - ->willReturnCallback(function ($value) use ($matcher) { - match ($matcher->getInvocationCount()) { - 1 => $this->assertEquals(123, $value), - 2 => $this->assertEquals(321, $value), - }; - }) - ; - - $this->collectorRegistryMock - ->expects($this->exactly(2)) - ->method('getGauge') - ->with(self::TEST_NAMESPACE, self::TEST_VARIABLE_NAME) - ->willReturn($gaugeMock) - ; - - $this->collector->measure(self::TEST_VARIABLE_NAME, $firstExpectedVariableValue); + $this->collector->timing(self::TEST_VARIABLE_NAME, $firstExpectedVariableValue); $this->collector->flush(); - $this->collector->measure(self::TEST_VARIABLE_NAME, $secondExpectedVariableValue); + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $firstExpectedVariableValue, $data->getValue()); + + $secondExpectedVariableValue = 321; + + $this->collector->timing(self::TEST_VARIABLE_NAME, $secondExpectedVariableValue); $this->collector->flush(); + + $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); + $this->assertSame((string) $secondExpectedVariableValue, $data->getValue()); } } diff --git a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php index 4374562..6497d98 100644 --- a/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php +++ b/tests/MetricsBundle/DependencyInjection/BeberleiMetricsExtensionTest.php @@ -140,11 +140,11 @@ public function testWithPrometheus(): void ->getMock() ; - $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock']]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'service' => 'prometheus_collector_registry_mock']]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); $this->assertInstanceOf(Prometheus::class, $collector); - $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'collectorRegistry')); + $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'registry')); $this->assertSame('', $this->getProperty($collector, 'namespace')); } @@ -164,11 +164,11 @@ public function testWithPrometheusAndWithNamespace(): void ->getMock() ; - $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', 'namespace' => $expectedNamespace]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'service' => 'prometheus_collector_registry_mock', 'namespace' => $expectedNamespace]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); $this->assertInstanceOf(Prometheus::class, $collector); - $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'collectorRegistry')); + $this->assertSame($prometheusCollectorRegistryMock, $this->getProperty($collector, 'registry')); $this->assertSame($expectedNamespace, $this->getProperty($collector, 'namespace')); } @@ -181,7 +181,7 @@ public function testWithPrometheusAndWithTags(): void ->getMock() ; - $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'prometheus_collector_registry' => 'prometheus_collector_registry_mock', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus', 'service' => 'prometheus_collector_registry_mock', 'tags' => $expectedTags]]], ['beberlei_metrics.collector.prometheus'], ['prometheus_collector_registry_mock' => $prometheusCollectorRegistryMock]); $collector = $container->get('beberlei_metrics.collector.prometheus'); $this->assertInstanceOf(Prometheus::class, $collector); @@ -190,9 +190,10 @@ public function testWithPrometheusAndWithTags(): void public function testValidationWhenTypeIsPrometheusAndPrometheusCollectorRegistryIsNotSpecified(): void { - $this->expectException(InvalidConfigurationException::class); - $this->expectExceptionMessage('The "prometheus_collector_registry" has to be specified to use a Prometheus'); - $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]]); + $container = $this->createContainer(['collectors' => ['prometheus' => ['type' => 'prometheus']]], ['beberlei_metrics.collector.prometheus']); + + $collector = $container->get('beberlei_metrics.collector.prometheus'); + $this->assertInstanceOf(Prometheus::class, $collector); } public function testWithInvalid(): void @@ -224,11 +225,8 @@ private function createContainer($configs, array $publicServices = [], array $ad return $container; } - private function getProperty(?object $object, string $property) + private function getProperty(?object $object, string $property): mixed { - $reflectionProperty = new \ReflectionProperty($object::class, $property); - $reflectionProperty->setAccessible(true); - - return $reflectionProperty->getValue($object); + return (new \ReflectionProperty($object::class, $property))->getValue($object); } } From 8679461259133e660970e6c50fff26118c142979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 14:35:37 +0100 Subject: [PATCH 27/32] composer: cleaning --- composer.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index e8b17cf..09097d2 100644 --- a/composer.json +++ b/composer.json @@ -23,19 +23,14 @@ "doctrine/dbal": "^2.0", "influxdb/influxdb-php": "^1.15", "jimdo/prometheus_client_php": "^0.5.1", - "symfony/config": "^5.4 || ^6.4 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.4 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0", "symfony/phpunit-bridge": "^6.4.4 || ^7.0" }, "conflict": { "doctrine/dbal": "<2", "influxdb/influxdb-php": "<1.15", "jimdo/prometheus_client_php": "<0.5.1", - "symfony/config": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/http-client": "<5.4", - "symfony/http-kernel": "<5.4" + "symfony/framework-bundle": "<5.4" }, "autoload": { "psr-4": { From 669d5a36580f0c8f86d6713b41e5dfd90eb47cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 17:07:38 +0100 Subject: [PATCH 28/32] doc: reword the readme --- README.md | 111 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index ad0fd99..091abc5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Metrics -[![Build Status](https://travis-ci.org/beberlei/metrics.svg?branch=master)](https://travis-ci.org/beberlei/metrics) - Simple library that abstracts different metrics collectors. I find this necessary to have a consistent and simple metrics API that doesn't cause vendor lock-in. @@ -11,20 +9,20 @@ It also ships with a Symfony Bundle. **This is not a library for displaying metr Currently supported backends: * Doctrine DBAL +* DogStatsD * Graphite -* InfluxDB -* Telegraf +* InfluxDb (version 1) * Logger (Psr\Log\LoggerInterface) * Null (Dummy that does nothing) * Prometheus * StatsD -* DogStatsD +* Telegraf ## Installation Using Composer: -```bash +``` composer require beberlei/metrics ``` @@ -33,16 +31,12 @@ composer require beberlei/metrics You can instantiate clients: ```php -increment('foo.bar'); $collector->decrement('foo.bar'); @@ -54,86 +48,107 @@ $value = 1234; $collector->measure('foo.bar', $value); ``` -Some backends defer sending and aggregate all information, make sure to call +All backends defer sending and aggregate all information, make sure to call flush: ```php -flush(); ``` ## Configuration ```php - ['all' => true], +]; -class AppKernel extends Kernel -{ - public function registerBundles() - { - //.. - $bundles[] = new \Beberlei\Bundle\MetricsBundle\BeberleiMetricsBundle(); - //.. - } -} ``` -Do Configuration: +Do some configuration: ```yaml # app/config/config.yml beberlei_metrics: - default: foo + default: statsd collectors: - foo: - type: statsd - dbal: - type: doctrine_dbal - connection: metrics # using the connection named "metrics" - monolog: - type: monolog influxdb: type: influxdb database: metrics - tags: + # If you want to use a custom database service + # It must be an instance of "InfluxDB\Database" + # In this case, you can omit de "database" option + # service: my.service.id + tags: # optional dc: "west" node_instance: "hermes10" prometheus: type: prometheus - prometheus_collector_registry: prometheus_collector_registry_service # using the Prometheus collector registry service named "prometheus_collector_registry_service" + # If you want to use a custom registry service + # It must be an instance of "Prometheus\CollectorRegistry" + # By default it uses an "Prometheus\Storage\InMemory" adapter + # service: my.service.id namespace: app_name # optional - tags: + tags: # optional dc: "west" node_instance: "hermes10" + statsd: + type: statsd + # host: localhost # default + # port: 8125 # default + # prefix: '' # default + dogstatsd: + type: dogstatsd + # host: localhost # default + # port: 8125 # default + # prefix: '' # default + dbal: + type: doctrine_dbal + # Use another connection, by default it uses the default connection + # connection: metrics + monolog: + type: monolog ``` -This adds collectors to the Metrics registry. The functions are automatically -included in the Bundle class so that in your code you can just start using the -convenient functions. Metrics are also added as services: +Then, you can inject the `Beberlei\Metrics\Collector\CollectorInterface` and +start using it: ```php -get('beberlei_metrics.collector.foo'); + public function doSomething(): void + { + $this->collector->increment('foo.bar'); + } +} ``` -and the default collector can be fetched: +The `Beberlei\Metrics\Collector\CollectorInterface` is automatically aliased to +the default collector. +If you want to inject a specific collector, you must use the `#[Target]` attribute: ```php -get('beberlei_metrics.collector'); +public function __construct( + #[Target('name_of_the_collector')] + CollectorInterface $memoryCollector, +) { ``` - From bfab645bd9c210e7b9b52ae345ff16c5cfcb175c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 1 Mar 2024 17:27:59 +0100 Subject: [PATCH 29/32] chore: remove the TaggableCollectorInterface --- README.md | 4 + src/Metrics/Collector/InfluxDbV1.php | 7 +- src/Metrics/Collector/NullCollector.php | 6 +- src/Metrics/Collector/Prometheus.php | 9 +- .../Collector/TaggableCollectorInterface.php | 15 ---- src/Metrics/Collector/Telegraf.php | 13 ++- .../BeberleiMetricsExtension.php | 72 ++++++++------- .../Resources/config/metrics.xml | 36 ++++---- tests/Metrics/Collector/InfluxDbV1Test.php | 41 +++++---- tests/Metrics/Collector/PrometheusTest.php | 88 ++++++++++--------- 10 files changed, 143 insertions(+), 148 deletions(-) delete mode 100644 src/Metrics/Collector/TaggableCollectorInterface.php diff --git a/README.md b/README.md index 091abc5..cffc412 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,10 @@ beberlei_metrics: influxdb: type: influxdb database: metrics + # host: localhost # option + # username: username # optional + # password: password # optional + # port: 8086 # optional # If you want to use a custom database service # It must be an instance of "InfluxDB\Database" # In this case, you can omit de "database" option diff --git a/src/Metrics/Collector/InfluxDbV1.php b/src/Metrics/Collector/InfluxDbV1.php index 3d8bde3..2330927 100644 --- a/src/Metrics/Collector/InfluxDbV1.php +++ b/src/Metrics/Collector/InfluxDbV1.php @@ -13,7 +13,7 @@ use InfluxDB\Exception; use InfluxDB\Point; -class InfluxDbV1 implements CollectorInterface, TaggableCollectorInterface +class InfluxDbV1 implements CollectorInterface { private array $data = []; @@ -61,9 +61,4 @@ public function flush(): void $this->data = []; } - - public function setTags(array $tags): void - { - $this->tags = $tags; - } } diff --git a/src/Metrics/Collector/NullCollector.php b/src/Metrics/Collector/NullCollector.php index 5920334..51560f0 100644 --- a/src/Metrics/Collector/NullCollector.php +++ b/src/Metrics/Collector/NullCollector.php @@ -9,7 +9,7 @@ namespace Beberlei\Metrics\Collector; -class NullCollector implements CollectorInterface, GaugeableCollectorInterface, TaggableCollectorInterface +class NullCollector implements CollectorInterface, GaugeableCollectorInterface { public function increment(string $variable, array $tags = []): void { @@ -34,8 +34,4 @@ public function gauge(string $variable, string|int $value, array $tags = []): vo public function flush(): void { } - - public function setTags(array $tags): void - { - } } diff --git a/src/Metrics/Collector/Prometheus.php b/src/Metrics/Collector/Prometheus.php index a2bf557..1478ebd 100644 --- a/src/Metrics/Collector/Prometheus.php +++ b/src/Metrics/Collector/Prometheus.php @@ -11,7 +11,7 @@ use Prometheus\CollectorRegistry; -class Prometheus implements CollectorInterface, GaugeableCollectorInterface, TaggableCollectorInterface +class Prometheus implements CollectorInterface, GaugeableCollectorInterface { private array $counters = []; private array $gauges = []; @@ -19,7 +19,7 @@ class Prometheus implements CollectorInterface, GaugeableCollectorInterface, Tag public function __construct( private readonly CollectorRegistry $registry, private readonly string $namespace = '', - private array $tags = [], + private readonly array $tags = [], ) { } @@ -76,11 +76,6 @@ public function flush(): void $this->counters = $this->gauges = []; } - public function setTags(array $tags): void - { - $this->tags = $tags; - } - private function normalizeVariable(string $variable): string { return str_replace(['.', ':'], ['_', '_'], $variable); diff --git a/src/Metrics/Collector/TaggableCollectorInterface.php b/src/Metrics/Collector/TaggableCollectorInterface.php deleted file mode 100644 index 31650b5..0000000 --- a/src/Metrics/Collector/TaggableCollectorInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -tags = http_build_query($tags, '', ','); + $this->tags = \strlen($this->tags) > 0 ? ',' . $this->tags : $this->tags; } public function measure(string $variable, int $value, array $tags = []): void @@ -67,12 +70,6 @@ public function flush(): void Box::box($this->doFlush(...)); } - public function setTags(array $tags): void - { - $this->tags = http_build_query($tags, '', ','); - $this->tags = \strlen($this->tags) > 0 ? ',' . $this->tags : $this->tags; - } - private function doFlush(): void { $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr, 1.0); diff --git a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php index 1d8bdd5..c297317 100644 --- a/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php +++ b/src/MetricsBundle/DependencyInjection/BeberleiMetricsExtension.php @@ -77,76 +77,82 @@ private function createCollector(ContainerBuilder $container, string $name, stri $definition->addTag(CollectorInterface::class); $definition->addTag('kernel.reset', ['method' => 'flush']); - if ($config['tags'] ?? []) { - $definition->addMethodCall('setTags', [$config['tags']]); - } + $tags = $config['tags'] ?? []; switch ($type) { - case 'doctrine_dbal': - $ref = $config['connection'] ? sprintf('doctrine.dbal.%s_connection', $config['connection']) : 'database_connection'; - $definition->replaceArgument(0, new Reference($ref)); - - return $definition; - case 'graphite': - $definition->replaceArgument(0, $config['host']); - $definition->replaceArgument(1, $config['port'] ?? 2003); - $definition->replaceArgument(2, $config['protocol'] ?? 'tcp'); - - return $definition; case 'influxdb_v1': if (!class_exists(\InfluxDB\Client::class)) { throw new \LogicException('The "influxdb/influxdb-php" package is required to use the "influxdb" collector.'); } + if ($config['service']) { - $definition->replaceArgument(0, new Reference($config['service'])); + $database = new Reference($config['service']); } else { $database = new ChildDefinition('beberlei_metrics.collector_proto.influxdb_v1.database'); - $database->replaceArgument(0, sprintf('influxdb://%s:%s@%s:%s/%s', + $database->replaceArgument('$dsn', sprintf('influxdb://%s:%s@%s:%s/%s', $config['username'], $config['password'], $config['host'], $config['port'] ?? 8086, $config['database'], )); - $definition->replaceArgument(0, $database); } - return $definition; - case 'logger': - case 'null': - case 'memory': + $definition->replaceArgument('$database', $database); + $definition->replaceArgument('$tags', $tags); + return $definition; case 'prometheus': if (!class_exists(CollectorRegistry::class)) { throw new \LogicException('The "promphp/prometheus_client_php" package is required to use the "prometheus" collector.'); } + if ($config['service']) { - $definition->replaceArgument(0, new Reference($config['service'])); + $registryId = $config['service']; } else { - $database = new ChildDefinition('beberlei_metrics.collector_proto.prometheus.registry'); - $container->setDefinition($id = 'beberlei_metrics.collector.' . $name . '.prometheus.registry', $database); + $container->setDefinition( + $registryId = 'beberlei_metrics.collector.' . $name . '.prometheus.registry', + new ChildDefinition('beberlei_metrics.collector_proto.prometheus.registry'), + ); - $definition->replaceArgument(0, new Reference($id)); if (!$container->hasAlias(CollectorRegistry::class)) { - $container->setAlias(CollectorRegistry::class, $id); + $container->setAlias(CollectorRegistry::class, $registryId); } } - $definition->replaceArgument(1, $config['namespace']); + $definition->replaceArgument('$registry', new Reference($registryId)); + $definition->replaceArgument('$namespace', $config['namespace']); + $definition->replaceArgument('$tags', $tags); + + return $definition; + case 'graphite': + $definition->replaceArgument('$host', $config['host']); + $definition->replaceArgument('$port', $config['port'] ?? 2003); + $definition->replaceArgument('$protocol', $config['protocol'] ?? 'tcp'); return $definition; case 'statsd': case 'dogstatsd': - $definition->replaceArgument(0, $config['host']); - $definition->replaceArgument(1, $config['port'] ?? 8125); - $definition->replaceArgument(2, $config['prefix']); + $definition->replaceArgument('$host', $config['host']); + $definition->replaceArgument('$port', $config['port'] ?? 8125); + $definition->replaceArgument('$prefix', $config['prefix']); return $definition; case 'telegraf': - $definition->replaceArgument(0, $config['host']); - $definition->replaceArgument(1, $config['port'] ?? 8125); - $definition->replaceArgument(2, $config['prefix']); + $definition->replaceArgument('$host', $config['host']); + $definition->replaceArgument('$port', $config['port'] ?? 8125); + $definition->replaceArgument('$prefix', $config['prefix']); + $definition->replaceArgument('$tags', $tags); + return $definition; + case 'doctrine_dbal': + $ref = $config['connection'] ? sprintf('doctrine.dbal.%s_connection', $config['connection']) : 'database_connection'; + $definition->replaceArgument('$conn', new Reference($ref)); + + return $definition; + case 'logger': + case 'memory': + case 'null': return $definition; default: throw new \InvalidArgumentException(sprintf('The type "%s" is not supported.', $type)); diff --git a/src/MetricsBundle/Resources/config/metrics.xml b/src/MetricsBundle/Resources/config/metrics.xml index fd1f01c..145d5b6 100644 --- a/src/MetricsBundle/Resources/config/metrics.xml +++ b/src/MetricsBundle/Resources/config/metrics.xml @@ -7,19 +7,19 @@ - + should be defined the extension - - - + should be defined the extension + should be defined the extension + should be defined the extension - + should be defined the extension - + should be defined the extension @@ -33,23 +33,25 @@ - - + should be defined the extension + should be defined the extension + should be defined the extension - - - + should be defined the extension + should be defined the extension + should be defined the extension - - - + should be defined the extension + should be defined the extension + should be defined the extension - - - + should be defined the extension + should be defined the extension + should be defined the extension + should be defined the extension diff --git a/tests/Metrics/Collector/InfluxDbV1Test.php b/tests/Metrics/Collector/InfluxDbV1Test.php index e072c2f..2690f31 100644 --- a/tests/Metrics/Collector/InfluxDbV1Test.php +++ b/tests/Metrics/Collector/InfluxDbV1Test.php @@ -19,30 +19,31 @@ class InfluxDbV1Test extends TestCase { private MockObject&Database $database; - private InfluxDbV1 $collector; - protected function setUp(): void { $this->database = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->getMock() ; - $this->collector = new InfluxDbV1($this->database); } public function testCollectIncrement(): void { + $collector = $this->createCollector([]); + $this->database->expects($this->once()) ->method('writePoints') ->with($this->isType('array')) ; - $this->collector->increment('series-name'); - $this->collector->flush(); + $collector->increment('series-name'); + $collector->flush(); } public function testCollectDecrement(): void { + $collector = $this->createCollector([]); + $this->database->expects($this->once()) ->method('writePoints') ->with($this->callback(function ($arg0) { @@ -59,12 +60,14 @@ public function testCollectDecrement(): void })) ; - $this->collector->decrement('series-name'); - $this->collector->flush(); + $collector->decrement('series-name'); + $collector->flush(); } public function testCollectTiming(): void { + $collector = $this->createCollector([]); + $this->database->expects($this->once()) ->method('writePoints') ->with($this->callback(function ($arg0) { @@ -81,12 +84,14 @@ public function testCollectTiming(): void })) ; - $this->collector->timing('series-name', 47); - $this->collector->flush(); + $collector->timing('series-name', 47); + $collector->flush(); } public function testCollectMeasure(): void { + $collector = $this->createCollector([]); + $this->database->expects($this->once()) ->method('writePoints') ->with($this->callback(function ($arg0) { @@ -103,13 +108,14 @@ public function testCollectMeasure(): void })) ; - $this->collector->measure('series-name', 47); - $this->collector->flush(); + $collector->measure('series-name', 47); + $collector->flush(); } public function testCollectMeasureWithTags(): void { $expectedTags = ['dc' => 'west', 'node' => 'nemesis101']; + $collector = $this->createCollector($expectedTags); $this->database->expects($this->once()) ->method('writePoints') @@ -127,13 +133,14 @@ public function testCollectMeasureWithTags(): void })) ; - $this->collector->setTags($expectedTags); - $this->collector->measure('series-name', 47); - $this->collector->flush(); + $collector->measure('series-name', 47); + $collector->flush(); } public function testCollectMeasureWithTagsMerged(): void { + $collector = $this->createCollector(['dc' => 'west', 'node' => 'nemesis101']); + $this->database->expects($this->once()) ->method('writePoints') ->with($this->callback(function ($arg0) { @@ -150,8 +157,12 @@ public function testCollectMeasureWithTagsMerged(): void })) ; - $collector = new InfluxDbV1($this->database, ['dc' => 'west', 'node' => 'nemesis101']); $collector->measure('series-name', 47, ['foo' => 'bar']); $collector->flush(); } + + private function createCollector(array $tags): InfluxDbV1 + { + return new InfluxDbV1($this->database, $tags); + } } diff --git a/tests/Metrics/Collector/PrometheusTest.php b/tests/Metrics/Collector/PrometheusTest.php index c8c1393..2bfe99a 100644 --- a/tests/Metrics/Collector/PrometheusTest.php +++ b/tests/Metrics/Collector/PrometheusTest.php @@ -19,13 +19,11 @@ class PrometheusTest extends TestCase private const TEST_NAMESPACE = 'some_metric_namespace'; private const TEST_VARIABLE_NAME = 'some_variable_name'; - private CollectorRegistry $collectorRegistry; - private Prometheus $collector; + private CollectorRegistry $registry; protected function setUp(): void { - $this->collectorRegistry = new CollectorRegistry(new InMemory(), false); - $this->collector = new Prometheus($this->collectorRegistry, self::TEST_NAMESPACE); + $this->registry = new CollectorRegistry(new InMemory(), false); } public function testMeasure(): void @@ -33,11 +31,11 @@ public function testMeasure(): void $expectedVariableValue = 123; $labels = []; - $this->collector->setTags($labels); - $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $expectedVariableValue, $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -48,11 +46,11 @@ public function testMeasureWithTags(): void $expectedVariableValue = 123; $labels = ['tag1' => 'value1', 'tag2' => 'value2']; - $this->collector->setTags($labels); - $this->collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->measure(self::TEST_VARIABLE_NAME, $expectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $expectedVariableValue, $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -62,11 +60,11 @@ public function testIncrement(): void { $labels = []; - $this->collector->setTags($labels); - $this->collector->increment(self::TEST_VARIABLE_NAME); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->increment(self::TEST_VARIABLE_NAME); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame('1', $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -76,11 +74,11 @@ public function testIncrementWithTags(): void { $labels = ['value1', 'value2']; - $this->collector->setTags($labels); - $this->collector->increment(self::TEST_VARIABLE_NAME); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->increment(self::TEST_VARIABLE_NAME); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame('1', $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -90,11 +88,11 @@ public function testDecrement(): void { $labels = []; - $this->collector->setTags($labels); - $this->collector->decrement(self::TEST_VARIABLE_NAME); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->decrement(self::TEST_VARIABLE_NAME); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame('-1', $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -104,11 +102,11 @@ public function testDecrementWithTags(): void { $labels = ['value1', 'value2']; - $this->collector->setTags($labels); - $this->collector->decrement(self::TEST_VARIABLE_NAME); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->decrement(self::TEST_VARIABLE_NAME); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame('-1', $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -119,11 +117,11 @@ public function testTiming(): void $expectedVariableValue = 123; $labels = []; - $this->collector->setTags($labels); - $this->collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $expectedVariableValue, $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -134,11 +132,11 @@ public function testTimingWithTags(): void $expectedVariableValue = 123; $labels = ['tag1' => 'value1', 'tag2' => 'value2']; - $this->collector->setTags($labels); - $this->collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); - $this->collector->flush(); + $collector = $this->createCollector($labels); + $collector->timing(self::TEST_VARIABLE_NAME, $expectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $expectedVariableValue, $data->getValue()); $this->assertSame($labels, $data->getLabelValues()); @@ -151,20 +149,26 @@ public function testFlushWhenCallsTwiceWithDifferentData(): void { $firstExpectedVariableValue = 123; - $this->collector->timing(self::TEST_VARIABLE_NAME, $firstExpectedVariableValue); - $this->collector->flush(); + $collector = $this->createCollector([]); + $collector->timing(self::TEST_VARIABLE_NAME, $firstExpectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $firstExpectedVariableValue, $data->getValue()); $secondExpectedVariableValue = 321; - $this->collector->timing(self::TEST_VARIABLE_NAME, $secondExpectedVariableValue); - $this->collector->flush(); + $collector->timing(self::TEST_VARIABLE_NAME, $secondExpectedVariableValue); + $collector->flush(); - $data = $this->collectorRegistry->getMetricFamilySamples()[0]->getSamples()[0]; + $data = $this->registry->getMetricFamilySamples()[0]->getSamples()[0]; $this->assertSame(self::TEST_NAMESPACE . '_' . self::TEST_VARIABLE_NAME, $data->getName()); $this->assertSame((string) $secondExpectedVariableValue, $data->getValue()); } + + private function createCollector(array $tags): Prometheus + { + return new Prometheus($this->registry, self::TEST_NAMESPACE, $tags); + } } From 4d8365b414817c963f1cc073998d5a8b57afd7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 23:36:11 +0100 Subject: [PATCH 30/32] example: add very basic integration --- composer.json | 8 +- examples/.castor/docker.php | 2 +- examples/.castor/qa.php | 2 +- examples/.env | 11 - examples/.gitignore | 5 - examples/README.md | 10 +- examples/assets/app.js | 10 - examples/assets/bootstrap.js | 5 - examples/assets/controllers.json | 15 - .../assets/controllers/hello_controller.js | 16 - examples/assets/styles/app.css | 3 - examples/castor.php | 10 +- examples/composer.json | 43 +- examples/composer.lock | 4884 +++++------------ examples/config/bundles.php | 5 +- examples/config/packages/asset_mapper.yaml | 5 - examples/config/packages/mailer.yaml | 3 - examples/config/packages/messenger.yaml | 24 - examples/config/packages/metrics.yaml | 31 + examples/config/packages/notifier.yaml | 12 - examples/config/packages/security.yaml | 39 - examples/config/packages/translation.yaml | 7 - examples/config/packages/twig.yaml | 2 + examples/config/packages/validator.yaml | 11 - examples/config/routes/security.yaml | 3 - examples/config/services.yaml | 19 +- examples/importmap.php | 24 - .../docker/docker-compose.builder.yml | 1 + .../infrastructure/docker/docker-compose.yml | 105 + .../php/frontend/etc/nginx/nginx.conf | 5 - .../docker/services/prometheus/prometheus.yml | 14 + .../docker/services/vector/vector.yaml | 62 + examples/migrations/.gitignore | 0 examples/migrations/Version20240302012809.php | 32 + examples/phpstan.neon | 2 +- examples/src/Controller/.gitignore | 0 .../src/Controller/HomepageController.php | 39 +- .../src/Controller/PrometheusController.php | 34 + examples/src/Entity/.gitignore | 0 examples/src/Entity/Metrics.php | 36 + examples/src/Repository/.gitignore | 0 examples/src/Repository/MetricsRepository.php | 30 + examples/symfony.lock | 109 +- examples/templates/base.html.twig | 1 - examples/templates/homepage/index.html.twig | 56 + examples/translations/.gitignore | 0 46 files changed, 1879 insertions(+), 3856 deletions(-) delete mode 100644 examples/assets/app.js delete mode 100644 examples/assets/bootstrap.js delete mode 100644 examples/assets/controllers.json delete mode 100644 examples/assets/controllers/hello_controller.js delete mode 100644 examples/assets/styles/app.css delete mode 100644 examples/config/packages/asset_mapper.yaml delete mode 100644 examples/config/packages/mailer.yaml delete mode 100644 examples/config/packages/messenger.yaml create mode 100644 examples/config/packages/metrics.yaml delete mode 100644 examples/config/packages/notifier.yaml delete mode 100644 examples/config/packages/security.yaml delete mode 100644 examples/config/packages/translation.yaml delete mode 100644 examples/config/packages/validator.yaml delete mode 100644 examples/config/routes/security.yaml delete mode 100644 examples/importmap.php create mode 100644 examples/infrastructure/docker/services/prometheus/prometheus.yml create mode 100644 examples/infrastructure/docker/services/vector/vector.yaml delete mode 100644 examples/migrations/.gitignore create mode 100644 examples/migrations/Version20240302012809.php delete mode 100644 examples/src/Controller/.gitignore create mode 100644 examples/src/Controller/PrometheusController.php delete mode 100644 examples/src/Entity/.gitignore create mode 100644 examples/src/Entity/Metrics.php delete mode 100644 examples/src/Repository/.gitignore create mode 100644 examples/src/Repository/MetricsRepository.php create mode 100644 examples/templates/homepage/index.html.twig delete mode 100644 examples/translations/.gitignore diff --git a/composer.json b/composer.json index 09097d2..7ff4fbe 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,14 @@ "php": ">=8.1", "doctrine/dbal": "^2.0", "influxdb/influxdb-php": "^1.15", - "jimdo/prometheus_client_php": "^0.5.1", + "promphp/prometheus_client_php": "^2", "symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0", "symfony/phpunit-bridge": "^6.4.4 || ^7.0" }, "conflict": { "doctrine/dbal": "<2", "influxdb/influxdb-php": "<1.15", - "jimdo/prometheus_client_php": "<0.5.1", + "promphp/prometheus_client_php": "<2", "symfony/framework-bundle": "<5.4" }, "autoload": { @@ -44,8 +44,10 @@ } }, "suggest": { + "doctrine/dbal": "For Doctrine DBAL integration", "influxdb/influxdb-php": "For InfluxDB integration", - "jimdo/prometheus_client_php": "For Prometheus integration" + "symfony/framework-bundle": "For Symfony integration", + "promphp/prometheus_client_php": "For Prometheus integration" }, "config": { "sort-packages": true diff --git a/examples/.castor/docker.php b/examples/.castor/docker.php index 3640ec7..99938d1 100644 --- a/examples/.castor/docker.php +++ b/examples/.castor/docker.php @@ -304,7 +304,7 @@ function create_default_context(): Context // If the directory does not exist, we create it. Otherwise, docker // will do, as root, and the user will not be able to write in it. if (!is_dir($composerCacheDir)) { - mkdir($composerCacheDir, 0777, true); + mkdir($composerCacheDir, 0o777, true); } } diff --git a/examples/.castor/qa.php b/examples/.castor/qa.php index 32d5202..b068c4d 100644 --- a/examples/.castor/qa.php +++ b/examples/.castor/qa.php @@ -16,7 +16,7 @@ function all(): int $phpstan = phpstan(); // $phpunit = phpunit(); - return max($cs, $phpstan/*, $phpunit*/); + return max($cs, $phpstan/* , $phpunit */); } #[AsTask(description: 'Installs tooling')] diff --git a/examples/.env b/examples/.env index 1bb8dab..a4f9456 100644 --- a/examples/.env +++ b/examples/.env @@ -28,14 +28,3 @@ APP_SECRET=acbff843d79416cbb1432679dc4f3ead # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" DATABASE_URL="postgresql://app:app@postgres:5432/app?serverVersion=16&charset=utf8" ###< doctrine/doctrine-bundle ### - -###> symfony/messenger ### -# Choose one of the transports below -# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages -# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages -MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 -###< symfony/messenger ### - -###> symfony/mailer ### -# MAILER_DSN=null://null -###< symfony/mailer ### diff --git a/examples/.gitignore b/examples/.gitignore index 2ea9c84..1a2a239 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -23,8 +23,3 @@ .phpunit.result.cache /phpunit.xml ###< symfony/phpunit-bridge ### - -###> symfony/asset-mapper ### -/public/assets/ -/assets/vendor/ -###< symfony/asset-mapper ### diff --git a/examples/README.md b/examples/README.md index 64af536..5749442 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,8 @@ -# My project +# Symfony demo application + +This application use +[jolicode/docker-starter](https://github.com/jolicode/docker-starter) to provide +a local development environment. ## Running the application locally @@ -7,8 +11,6 @@ A Docker environment is provided and requires you to have these tools available: * Docker - * Bash - * PHP >= 8.1 * [Castor](https://github.com/jolicode/castor#installation) #### Castor @@ -50,7 +52,7 @@ This IP is probably `127.0.0.1` unless you run Docker in a special VM (like dock > The router binds port 80 and 443, that's why it will work with `127.0.0.1` ``` -echo '127.0.0.1 app.test www.app.test' | sudo tee -a /etc/hosts +echo '127.0.0.1 symfony-metrics.test grafana.symfony-metrics.test' | sudo tee -a /etc/hosts ``` ### Starting the stack diff --git a/examples/assets/app.js b/examples/assets/app.js deleted file mode 100644 index 8725cc5..0000000 --- a/examples/assets/app.js +++ /dev/null @@ -1,10 +0,0 @@ -import './bootstrap.js'; -/* - * Welcome to your app's main JavaScript file! - * - * This file will be included onto the page via the importmap() Twig function, - * which should already be in your base.html.twig. - */ -import './styles/app.css'; - -console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉'); diff --git a/examples/assets/bootstrap.js b/examples/assets/bootstrap.js deleted file mode 100644 index d4e50c9..0000000 --- a/examples/assets/bootstrap.js +++ /dev/null @@ -1,5 +0,0 @@ -import { startStimulusApp } from '@symfony/stimulus-bundle'; - -const app = startStimulusApp(); -// register any custom, 3rd party controllers here -// app.register('some_controller_name', SomeImportedController); diff --git a/examples/assets/controllers.json b/examples/assets/controllers.json deleted file mode 100644 index 29ea244..0000000 --- a/examples/assets/controllers.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "controllers": { - "@symfony/ux-turbo": { - "turbo-core": { - "enabled": true, - "fetch": "eager" - }, - "mercure-turbo-stream": { - "enabled": false, - "fetch": "eager" - } - } - }, - "entrypoints": [] -} diff --git a/examples/assets/controllers/hello_controller.js b/examples/assets/controllers/hello_controller.js deleted file mode 100644 index e847027..0000000 --- a/examples/assets/controllers/hello_controller.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Controller } from '@hotwired/stimulus'; - -/* - * This is an example Stimulus controller! - * - * Any element with a data-controller="hello" attribute will cause - * this controller to be executed. The name "hello" comes from the filename: - * hello_controller.js -> "hello" - * - * Delete this file or adapt it for your use! - */ -export default class extends Controller { - connect() { - this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js'; - } -} diff --git a/examples/assets/styles/app.css b/examples/assets/styles/app.css deleted file mode 100644 index dd6181a..0000000 --- a/examples/assets/styles/app.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: skyblue; -} diff --git a/examples/castor.php b/examples/castor.php index dbe4049..6ac71e5 100644 --- a/examples/castor.php +++ b/examples/castor.php @@ -30,13 +30,10 @@ function create_default_variables(): array return [ 'project_name' => 'symfony-metrics', 'root_domain' => 'symfony-metrics.test', - 'extra_domains' => [ - 'grafana.symfony-metrics.test', - ], ]; } -#[AsTask(description: 'Builds and starts the infrastructure, then install the application (composer, yarn, ...)')] +#[AsTask(description: 'Builds and starts the infrastructure, then install the application (composer, ...)')] function start(): void { io()->title('Starting the stack'); @@ -56,7 +53,7 @@ function start(): void about(); } -#[AsTask(description: 'Installs the application (composer, yarn, ...)', namespace: 'app', aliases: ['install'])] +#[AsTask(description: 'Installs the application (composer, ...)', namespace: 'app', aliases: ['install'])] function install(): void { io()->title('Installing the application'); @@ -64,9 +61,6 @@ function install(): void io()->section('Installing PHP dependencies'); docker_compose_run('composer install -n --prefer-dist --optimize-autoloader'); - io()->section('Installing importmap'); - docker_compose_run('bin/console importmap:install'); - qa\install(); } diff --git a/examples/composer.json b/examples/composer.json index 8d11ee5..1dd83d7 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -3,47 +3,33 @@ "license": "proprietary", "minimum-stability": "stable", "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "/metrics" + } + ], "require": { - "php": ">=8.2", + "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*", + "beberlei/metrics": "*@dev", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.11", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.0", + "influxdb/influxdb-php": "^1.15", "phpdocumentor/reflection-docblock": "^5.3", "phpstan/phpdoc-parser": "^1.26", - "symfony/asset": "7.0.*", - "symfony/asset-mapper": "7.0.*", + "promphp/prometheus_client_php": "^2.10", "symfony/console": "7.0.*", - "symfony/doctrine-messenger": "7.0.*", "symfony/dotenv": "7.0.*", - "symfony/expression-language": "7.0.*", "symfony/flex": "^2", - "symfony/form": "7.0.*", "symfony/framework-bundle": "7.0.*", - "symfony/http-client": "7.0.*", - "symfony/intl": "7.0.*", - "symfony/mailer": "7.0.*", - "symfony/mime": "7.0.*", "symfony/monolog-bundle": "^3.0", - "symfony/notifier": "7.0.*", - "symfony/process": "7.0.*", - "symfony/property-access": "7.0.*", - "symfony/property-info": "7.0.*", "symfony/runtime": "7.0.*", - "symfony/security-bundle": "7.0.*", - "symfony/serializer": "7.0.*", - "symfony/stimulus-bundle": "^2.16", - "symfony/string": "7.0.*", - "symfony/translation": "7.0.*", - "symfony/twig-bundle": "7.0.*", - "symfony/ux-turbo": "^2.16", - "symfony/validator": "7.0.*", - "symfony/web-link": "7.0.*", - "symfony/yaml": "7.0.*", - "twig/extra-bundle": "^2.12|^3.0", - "twig/twig": "^2.12|^3.0" + "symfony/uid": "7.0.*", + "symfony/yaml": "7.0.*" }, "config": { "allow-plugins": { @@ -76,8 +62,7 @@ "scripts": { "auto-scripts": { "cache:clear": "symfony-cmd", - "assets:install %PUBLIC_DIR%": "symfony-cmd", - "importmap:install": "symfony-cmd" + "assets:install %PUBLIC_DIR%": "symfony-cmd" }, "post-install-cmd": [ "@auto-scripts" @@ -97,8 +82,6 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "symfony/browser-kit": "7.0.*", - "symfony/css-selector": "7.0.*", "symfony/debug-bundle": "7.0.*", "symfony/maker-bundle": "^1.0", "symfony/phpunit-bridge": "^7.0", diff --git a/examples/composer.lock b/examples/composer.lock index 7e98a61..be4b0da 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -4,88 +4,74 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a081757e3f87e400b4b604ce219bee18", + "content-hash": "8d17d049d9055c18f7e29bb59065459f", "packages": [ { - "name": "composer/semver", - "version": "3.4.0", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" - }, + "name": "beberlei/metrics", + "version": "dev-massive-code-grooming", "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", - "shasum": "" + "type": "path", + "url": "/metrics", + "reference": "e27a066968ab3726df32364b8afe679562672783" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "psr/log": "^1.0 || ^2.0 || ^3.0" + }, + "conflict": { + "doctrine/dbal": "<2", + "influxdb/influxdb-php": "<1.15", + "promphp/prometheus_client_php": "<2", + "symfony/framework-bundle": "<5.4" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "doctrine/dbal": "^2.0", + "influxdb/influxdb-php": "^1.15", + "php": ">=8.1", + "promphp/prometheus_client_php": "^2", + "symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0" + }, + "suggest": { + "doctrine/dbal": "For Doctrine DBAL integration", + "influxdb/influxdb-php": "For InfluxDB integration", + "promphp/prometheus_client_php": "For Prometheus integration", + "symfony/framework-bundle": "For Symfony integration" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" + "autoload": { + "psr-4": { + "Beberlei\\Metrics\\": "src/Metrics", + "Beberlei\\Bundle\\MetricsBundle\\": "src/MetricsBundle" } }, - "autoload": { + "autoload-dev": { "psr-4": { - "Composer\\Semver\\": "src" + "Beberlei\\Bundle\\MetricsBundle\\": "src/MetricsBundle" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "role": "Project Founder" }, { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info", + "role": "Lead Developer" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "Simple library to talk to metrics collector services.", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } + "logging", + "metrics" ], - "time": "2023-08-31T09:50:34+00:00" + "transport-options": { + "relative": false + } }, { "name": "doctrine/cache", @@ -1306,40 +1292,256 @@ "time": "2023-08-16T21:49:04+00:00" }, { - "name": "egulias/email-validator", - "version": "4.0.2", + "name": "guzzlehttp/guzzle", + "version": "7.8.1", "source": { "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", "shasum": "" }, "require": { - "doctrine/lexer": "^2.0 || ^3.0", - "php": ">=8.1", - "symfony/polyfill-intl-idn": "^1.26" + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^5.12" + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:35:24+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:19:20+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { "psr-4": { - "Egulias\\EmailValidator\\": "src" + "GuzzleHttp\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1348,29 +1550,138 @@ ], "authors": [ { - "name": "Eduardo Gulias Davis" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" ], "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.2" }, "funding": [ { - "url": "https://github.com/egulias", + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:05:35+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" } ], - "time": "2023-10-06T06:47:41+00:00" + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "abandoned": true, + "time": "2020-12-26T17:45:17+00:00" }, { "name": "monolog/monolog", @@ -1688,6 +1999,73 @@ }, "time": "2024-02-23T16:05:55+00:00" }, + { + "name": "promphp/prometheus_client_php", + "version": "v2.10.0", + "source": { + "type": "git", + "url": "https://github.com/PromPHP/prometheus_client_php.git", + "reference": "a09ea80ec1ec26dd1d4853e9af2a811e898dbfeb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/a09ea80ec1ec26dd1d4853e9af2a811e898dbfeb", + "reference": "a09ea80ec1ec26dd1d4853e9af2a811e898dbfeb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "endclothing/prometheus_client_php": "*", + "jimdo/prometheus_client_php": "*", + "lkaemmerling/prometheus_client_php": "*" + }, + "require-dev": { + "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": { + "ext-apc": "Required if using APCu.", + "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-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Prometheus\\": "src/Prometheus/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Lukas Kämmerling", + "email": "kontakt@lukas-kaemmerling.de" + } + ], + "description": "Prometheus instrumentation library for PHP applications.", + "support": { + "issues": "https://github.com/PromPHP/prometheus_client_php/issues", + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.10.0" + }, + "time": "2024-02-01T13:28:34+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -1737,54 +2115,6 @@ }, "time": "2021-02-03T23:26:27+00:00" }, - { - "name": "psr/clock", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Clock\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", - "keywords": [ - "clock", - "now", - "psr", - "psr-20", - "time" - ], - "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" - }, - "time": "2022-11-25T14:36:26+00:00" - }, { "name": "psr/container", "version": "2.0.2", @@ -1889,34 +2219,32 @@ "time": "2019-01-08T18:20:26+00:00" }, { - "name": "psr/link", - "version": "2.0.1", + "name": "psr/http-client", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/link.git", - "reference": "84b159194ecfd7eaa472280213976e96415433f7" + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/link/zipball/84b159194ecfd7eaa472280213976e96415433f7", - "reference": "84b159194ecfd7eaa472280213976e96415433f7", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { - "php": ">=8.0.0" - }, - "suggest": { - "fig/link-util": "Provides some useful PSR-13 utilities" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Link\\": "src/" + "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1926,50 +2254,49 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for HTTP links", - "homepage": "https://github.com/php-fig/link", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ "http", - "http-link", - "link", + "http-client", "psr", - "psr-13", - "rest" + "psr-18" ], "support": { - "source": "https://github.com/php-fig/link/tree/2.0.1" + "source": "https://github.com/php-fig/http-client" }, - "time": "2021-03-11T23:00:27+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { - "name": "psr/log", - "version": "3.0.0", + "name": "psr/http-factory", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1982,51 +2309,49 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "log", + "factory", + "http", + "message", "psr", - "psr-3" + "psr-17", + "psr-7", + "request", + "response" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2023-04-10T20:10:41+00:00" }, { - "name": "symfony/asset", - "version": "v7.0.3", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/symfony/asset.git", - "reference": "3ae493792fc17cc31b84e231f30f2d154575f171" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/3ae493792fc17cc31b84e231f30f2d154575f171", - "reference": "3ae493792fc17cc31b84e231f30f2d154575f171", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/http-foundation": "<6.4" - }, - "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Asset\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2034,76 +2359,100 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", - "homepage": "https://symfony.com", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], "support": { - "source": "https://github.com/symfony/asset/tree/v7.0.3" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2024-01-23T15:02:46+00:00" + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" }, { - "name": "symfony/asset-mapper", - "version": "v7.0.4", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/asset-mapper.git", - "reference": "7cd421551f53849f641a1a3499392ed5c81598a8" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/7cd421551f53849f641a1a3499392ed5c81598a8", - "reference": "7cd421551f53849f641a1a3499392ed5c81598a8", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "composer/semver": "^3.0", - "php": ">=8.2", - "symfony/filesystem": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0" - }, - "conflict": { - "symfony/framework-bundle": "<6.4" + "php": ">=5.6" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/event-dispatcher-contracts": "^3.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\AssetMapper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2112,34 +2461,16 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" } ], - "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", - "homepage": "https://symfony.com", + "description": "A polyfill for getallheaders.", "support": { - "source": "https://github.com/symfony/asset-mapper/tree/v7.0.4" + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, - "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-02-22T20:27:20+00:00" + "time": "2019-03-08T08:55:37+00:00" }, { "name": "symfony/cache", @@ -2313,80 +2644,6 @@ ], "time": "2023-09-25T12:52:38+00:00" }, - { - "name": "symfony/clock", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/clock.git", - "reference": "1c680e565dc0044d8ed3baeb57835fcacd9c6aed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/1c680e565dc0044d8ed3baeb57835fcacd9c6aed", - "reference": "1c680e565dc0044d8ed3baeb57835fcacd9c6aed", - "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.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, { "name": "symfony/config", "version": "v7.0.4", @@ -2809,93 +3066,21 @@ "time": "2024-02-04T16:21:40+00:00" }, { - "name": "symfony/doctrine-messenger", + "name": "symfony/dotenv", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca" + "url": "https://github.com/symfony/dotenv.git", + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca", - "reference": "5a9ebba1b0be17af7b1e6b6433ad2cb6e35e97ca", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", "shasum": "" }, "require": { - "doctrine/dbal": "^3.6|^4", - "php": ">=8.2", - "symfony/messenger": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "doctrine/persistence": "<1.3" - }, - "require-dev": { - "doctrine/persistence": "^1.3|^2|^3", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" - }, - "type": "symfony-messenger-bridge", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Doctrine Messenger Bridge", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-22T20:27:20+00:00" - }, - { - "name": "symfony/dotenv", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", - "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", - "shasum": "" - }, - "require": { - "php": ">=8.2" + "php": ">=8.2" }, "conflict": { "symfony/console": "<6.4", @@ -3185,69 +3370,6 @@ ], "time": "2023-05-23T14:45:45+00:00" }, - { - "name": "symfony/expression-language", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/expression-language.git", - "reference": "0877c599cb260c9614f9229c0a2090d6919fd621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/0877c599cb260c9614f9229c0a2090d6919fd621", - "reference": "0877c599cb260c9614f9229c0a2090d6919fd621", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/cache": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ExpressionLanguage\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an engine that can compile and evaluate expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/expression-language/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, { "name": "symfony/filesystem", "version": "v7.0.3", @@ -3440,102 +3562,6 @@ ], "time": "2024-02-05T18:04:53+00:00" }, - { - "name": "symfony/form", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/form.git", - "reference": "5cfe85c74caf924c7cec2134e169320b464ede84" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/5cfe85c74caf924c7cec2134e169320b464ede84", - "reference": "5cfe85c74caf924c7cec2134e169320b464ede84", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/options-resolver": "^6.4|^7.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", - "symfony/error-handler": "<6.4", - "symfony/framework-bundle": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4" - }, - "require-dev": { - "doctrine/collections": "^1.0|^2.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/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/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Form\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows to easily create, process and reuse HTML forms", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/form/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-12T11:15:03+00:00" - }, { "name": "symfony/framework-bundle", "version": "v7.0.4", @@ -3683,54 +3709,42 @@ "time": "2024-02-26T07:52:39+00:00" }, { - "name": "symfony/http-client", + "name": "symfony/http-foundation", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "8384876f49a2316a63f88a9cd12436de6936bee6" + "url": "https://github.com/symfony/http-foundation.git", + "reference": "439fdfdd344943254b1ef6278613e79040548045" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/8384876f49a2316a63f88a9cd12436de6936bee6", - "reference": "8384876f49a2316a63f88a9cd12436de6936bee6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/439fdfdd344943254b1ef6278613e79040548045", + "reference": "439fdfdd344943254b1ef6278613e79040548045", "shasum": "" }, "require": { "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/http-client-contracts": "^3", - "symfony/service-contracts": "^2.5|^3" + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" }, "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.4" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\HttpClient\\": "" + "Symfony\\Component\\HttpFoundation\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3742,21 +3756,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", - "keywords": [ - "http" - ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.0.4" + "source": "https://github.com/symfony/http-foundation/tree/v7.0.4" }, "funding": [ { @@ -3772,1917 +3783,80 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-02-08T19:22:56+00:00" }, { - "name": "symfony/http-client-contracts", - "version": "v3.4.0", + "name": "symfony/http-kernel", + "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "1ee70e699b41909c209a0c930f11034b93578654" + "url": "https://github.com/symfony/http-kernel.git", + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", - "reference": "1ee70e699b41909c209a0c930f11034b93578654", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/065e2234d907c0fc4fc942bf223f7cfc016d0ac7", + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.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": "2023-07-30T20:28:31+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "439fdfdd344943254b1ef6278613e79040548045" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/439fdfdd344943254b1ef6278613e79040548045", - "reference": "439fdfdd344943254b1ef6278613e79040548045", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" - }, - "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4" - }, - "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": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-08T19:22:56+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/065e2234d907c0fc4fc942bf223f7cfc016d0ac7", - "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", - "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", - "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.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": "^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": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^6.4.4|^7.0.4", - "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-exporter": "^6.4|^7.0", - "twig/twig": "^3.0.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a structured process for converting a Request into a Response", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-27T06:35:35+00:00" - }, - { - "name": "symfony/intl", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/intl.git", - "reference": "295995df4acf6790a35b9ce6ec32b313efb11ff8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/295995df4acf6790a35b9ce6ec32b313efb11ff8", - "reference": "295995df4acf6790a35b9ce6ec32b313efb11ff8", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Intl\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Eriksen Costa", - "email": "eriksen.costa@infranology.com.br" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides access to the localization data of the ICU library", - "homepage": "https://symfony.com", - "keywords": [ - "i18n", - "icu", - "internationalization", - "intl", - "l10n", - "localization" - ], - "support": { - "source": "https://github.com/symfony/intl/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, - { - "name": "symfony/mailer", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/mailer.git", - "reference": "72e16d87bf50a3ce195b9470c06bb9d7b816ea85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/72e16d87bf50a3ce195b9470c06bb9d7b816ea85", - "reference": "72e16d87bf50a3ce195b9470c06bb9d7b816ea85", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.2", - "psr/event-dispatcher": "^1", - "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/twig-bridge": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mailer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps sending emails", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/mailer/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-03T21:34:19+00:00" - }, - { - "name": "symfony/messenger", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/messenger.git", - "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/804a8997f93313a8f7ed19e8cca3b44fdd18bdec", - "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/event-dispatcher-contracts": "<2.5", - "symfony/framework-bundle": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/serializer": "<6.4" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\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/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-26T07:52:39+00:00" - }, - { - "name": "symfony/mime", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716", - "reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1|^4", - "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mime\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows manipulating MIME messages", - "homepage": "https://symfony.com", - "keywords": [ - "mime", - "mime-type" - ], - "support": { - "source": "https://github.com/symfony/mime/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T08:34:29+00:00" - }, - { - "name": "symfony/monolog-bridge", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", - "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", - "shasum": "" - }, - "require": { - "monolog/monolog": "^3", - "php": ">=8.2", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/security-core": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mailer": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" - }, - "type": "symfony-bridge", - "autoload": { - "psr-4": { - "Symfony\\Bridge\\Monolog\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides integration for Monolog with various Symfony components", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, - { - "name": "symfony/monolog-bundle", - "version": "v3.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "shasum": "" - }, - "require": { - "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=7.2.5", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" - }, - "require-dev": { - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.3 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony MonologBundle", - "homepage": "https://symfony.com", - "keywords": [ - "log", - "logging" - ], - "support": { - "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.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": "2023-11-06T17:08:13+00:00" - }, - { - "name": "symfony/notifier", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/notifier.git", - "reference": "515326dab227ecd3198b04df50edf949f7767a9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/notifier/zipball/515326dab227ecd3198b04df50edf949f7767a9a", - "reference": "515326dab227ecd3198b04df50edf949f7767a9a", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3" - }, - "conflict": { - "symfony/event-dispatcher": "<6.4", - "symfony/event-dispatcher-contracts": "<2.5", - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4" - }, - "require-dev": { - "symfony/event-dispatcher-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Notifier\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Sends notifications via one or more channels (email, SMS, ...)", - "homepage": "https://symfony.com", - "keywords": [ - "notification", - "notifier" - ], - "support": { - "source": "https://github.com/symfony/notifier/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v7.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.0.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": "2023-08-08T10:20:21+00:00" - }, - { - "name": "symfony/password-hasher", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/password-hasher.git", - "reference": "0eba656c16ecdf5588b3ddd2b2337b06173d839f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/0eba656c16ecdf5588b3ddd2b2337b06173d839f", - "reference": "0eba656c16ecdf5588b3ddd2b2337b06173d839f", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/security-core": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PasswordHasher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Robin Chalas", - "email": "robin.chalas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides password hashing utilities", - "homepage": "https://symfony.com", - "keywords": [ - "hashing", - "password" - ], - "support": { - "source": "https://github.com/symfony/password-hasher/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-12T11:15:03+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.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-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-icu", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/07094a28851a49107f3ab4f9120ca2975a64b6e1", - "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance and support of other locales than \"en\"" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Icu\\": "" - }, - "classmap": [ - "Resources/stubs" - ], - "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": "Symfony polyfill for intl's ICU-related data and classes", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "icu", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.29.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-01-29T20:12:16+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.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-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.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-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.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-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-php83", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "symfony/polyfill-php80": "^1.14" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.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-01-29T20:11:03+00:00" - }, - { - "name": "symfony/process", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-22T20:27:20+00:00" - }, - { - "name": "symfony/property-access", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "44e3746d4de8d0961a44ee332c74dd0918266127" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/44e3746d4de8d0961a44ee332c74dd0918266127", - "reference": "44e3746d4de8d0961a44ee332c74dd0918266127", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/property-info": "^6.4|^7.0" - }, - "require-dev": { - "symfony/cache": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides functions to read and write from/to an object or array using a simple string notation", - "homepage": "https://symfony.com", - "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property-path", - "reflection" - ], - "support": { - "source": "https://github.com/symfony/property-access/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-16T13:44:10+00:00" - }, - { - "name": "symfony/property-info", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-info.git", - "reference": "e160f92ea827243abf2dbf36b8460b1377194406" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/e160f92ea827243abf2dbf36b8460b1377194406", - "reference": "e160f92ea827243abf2dbf36b8460b1377194406", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/string": "^6.4|^7.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<5.2", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/dependency-injection": "<6.4", - "symfony/serializer": "<6.4" - }, - "require-dev": { - "phpdocumentor/reflection-docblock": "^5.2", - "phpstan/phpdoc-parser": "^1.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyInfo\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Extracts information about PHP class' properties using metadata of popular sources", - "homepage": "https://symfony.com", - "keywords": [ - "doctrine", - "phpdoc", - "property", - "symfony", - "type", - "validator" - ], - "support": { - "source": "https://github.com/symfony/property-info/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, - { - "name": "symfony/routing", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "858b26756ffc35a11238b269b484ee3a393a74d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/858b26756ffc35a11238b269b484ee3a393a74d3", - "reference": "858b26756ffc35a11238b269b484ee3a393a74d3", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T13:55:15+00:00" - }, - { - "name": "symfony/runtime", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/runtime.git", - "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", - "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=8.2" - }, - "conflict": { - "symfony/dotenv": "<6.4" - }, - "require-dev": { - "composer/composer": "^2.6", - "symfony/console": "^6.4|^7.0", - "symfony/dotenv": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Runtime\\": "", - "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Enables decoupling PHP applications from global state", - "homepage": "https://symfony.com", - "keywords": [ - "runtime" - ], - "support": { - "source": "https://github.com/symfony/runtime/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, - { - "name": "symfony/security-bundle", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-bundle.git", - "reference": "1755fb50e4da0a0013569752be763d208d690bcd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/1755fb50e4da0a0013569752be763d208d690bcd", - "reference": "1755fb50e4da0a0013569752be763d208d690bcd", - "shasum": "" - }, - "require": { - "composer-runtime-api": ">=2.1", - "ext-xml": "*", - "php": ">=8.2", - "symfony/clock": "^6.4|^7.0", - "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/password-hasher": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", "symfony/console": "<6.4", - "symfony/framework-bundle": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", "symfony/http-client": "<6.4", - "symfony/ldap": "<6.4", - "symfony/serializer": "<6.4", - "symfony/twig-bundle": "<6.4", - "symfony/validator": "<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": { - "symfony/asset": "^6.4|^7.0", + "psr/cache": "^1.0|^2.0|^3.0", "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", "symfony/console": "^6.4|^7.0", "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", "symfony/dom-crawler": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/twig-bundle": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", "symfony/validator": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.0.4", - "web-token/jwt-checker": "^3.1", - "web-token/jwt-signature-algorithm-ecdsa": "^3.1", - "web-token/jwt-signature-algorithm-eddsa": "^3.1", - "web-token/jwt-signature-algorithm-hmac": "^3.1", - "web-token/jwt-signature-algorithm-none": "^3.1", - "web-token/jwt-signature-algorithm-rsa": "^3.1" + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, - "type": "symfony-bundle", + "type": "library", "autoload": { "psr-4": { - "Symfony\\Bundle\\SecurityBundle\\": "" + "Symfony\\Component\\HttpKernel\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -5702,10 +3876,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", + "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.0.4" + "source": "https://github.com/symfony/http-kernel/tree/v7.0.4" }, "funding": [ { @@ -5721,52 +3895,46 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-02-27T06:35:35+00:00" }, { - "name": "symfony/security-core", + "name": "symfony/monolog-bridge", "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/security-core.git", - "reference": "72b9d961a5dcd21e6bc29b99df51a9000a15dde0" + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/72b9d961a5dcd21e6bc29b99df51a9000a15dde0", - "reference": "72b9d961a5dcd21e6bc29b99df51a9000a15dde0", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", + "reference": "5d4f188e60d1e38a1d9d4bb6fbbbc13111dff2b1", "shasum": "" }, "require": { + "monolog/monolog": "^3", "php": ">=8.2", - "symfony/event-dispatcher-contracts": "^2.5|^3", - "symfony/password-hasher": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/event-dispatcher": "<6.4", + "symfony/console": "<6.4", "symfony/http-foundation": "<6.4", - "symfony/ldap": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/validator": "<6.4" + "symfony/security-core": "<6.4" }, "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "psr/container": "^1.1|^2.0", - "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/validator": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/mailer": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, - "type": "library", + "type": "symfony-bridge", "autoload": { "psr-4": { - "Symfony\\Component\\Security\\Core\\": "" + "Symfony\\Bridge\\Monolog\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -5786,10 +3954,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Security Component - Core Library", + "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.0.3" + "source": "https://github.com/symfony/monolog-bridge/tree/v7.0.3" }, "funding": [ { @@ -5808,33 +3976,41 @@ "time": "2024-01-23T15:02:46+00:00" }, { - "name": "symfony/security-csrf", - "version": "v7.0.3", + "name": "symfony/monolog-bundle", + "version": "v3.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/security-csrf.git", - "reference": "f0f724e599f069b768e335e4bdf795726c7dfe8e" + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/f0f724e599f069b768e335e4bdf795726c7dfe8e", - "reference": "f0f724e599f069b768e335e4bdf795726c7dfe8e", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/security-core": "^6.4|^7.0" - }, - "conflict": { - "symfony/http-foundation": "<6.4" + "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", + "php": ">=7.2.5", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "symfony/http-foundation": "^6.4|^7.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } }, - "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Security\\Csrf\\": "" + "Symfony\\Bundle\\MonologBundle\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -5854,10 +4030,15 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Security Component - CSRF Library", + "description": "Symfony MonologBundle", "homepage": "https://symfony.com", + "keywords": [ + "log", + "logging" + ], "support": { - "source": "https://github.com/symfony/security-csrf/tree/v7.0.3" + "issues": "https://github.com/symfony/monolog-bundle/issues", + "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" }, "funding": [ { @@ -5873,59 +4054,42 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2023-11-06T17:08:13+00:00" }, { - "name": "symfony/security-http", - "version": "v7.0.4", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/security-http.git", - "reference": "f3a70a937128f47366821a9f4b5dbfaa0ba9c862" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/f3a70a937128f47366821a9f4b5dbfaa0ba9c862", - "reference": "f3a70a937128f47366821a9f4b5dbfaa0ba9c862", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/clock": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/http-client-contracts": "<3.0", - "symfony/security-bundle": "<6.4", - "symfony/security-csrf": "<6.4" + "php": ">=7.1" }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-client-contracts": "^3.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "web-token/jwt-checker": "^3.1", - "web-token/jwt-signature-algorithm-ecdsa": "^3.1" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Security\\Http\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5933,18 +4097,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Security Component - HTTP Integration", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/security-http/tree/v7.0.4" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" }, "funding": [ { @@ -5960,66 +4132,44 @@ "type": "tidelift" } ], - "time": "2024-02-26T07:52:39+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/serializer", - "version": "v7.0.4", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", - "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8" + "php": ">=7.1" }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<6.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/uid": "<6.4", - "symfony/validator": "<6.4", - "symfony/yaml": "<6.4" + "suggest": { + "ext-intl": "For best performance" }, - "require-dev": { - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "seld/jsonlint": "^1.10", - "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/error-handler": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^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-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", - "symfony/yaml": "^6.4|^7.0" + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } }, - "type": "library", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Serializer\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6028,18 +4178,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/serializer/tree/v7.0.4" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" }, "funding": [ { @@ -6055,46 +4213,45 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.4.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0" + "php": ">=7.1" }, - "conflict": { - "ext-psr": "<1.1|>=2" + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6110,18 +4267,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -6137,60 +4293,68 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/stimulus-bundle", - "version": "v2.16.0", + "name": "symfony/polyfill-php83", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/stimulus-bundle.git", - "reference": "6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634", - "reference": "6add4bdab1b9df4f2b2532a9dcb7b2f26dbba634", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/deprecation-contracts": "^2.0|^3.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "twig/twig": "^2.15.3|~3.8.0" + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" }, - "require-dev": { - "symfony/asset-mapper": "^6.3|^7.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "zenstruck/browser": "^1.4" + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } }, - "type": "symfony-bundle", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\UX\\StimulusBundle\\": "src" - } + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Integration with your Symfony app & Stimulus!", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "symfony-ux" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/stimulus-bundle/tree/v2.16.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" }, "funding": [ { @@ -6206,34 +4370,45 @@ "type": "tidelift" } ], - "time": "2024-02-29T16:20:46+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/stopwatch", - "version": "v7.0.3", + "name": "symfony/polyfill-uuid", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", - "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/3abdd21b0ceaa3000ee950097bc3cf9efc137853", + "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/service-contracts": "^2.5|^3" + "php": ">=7.1" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Uuid\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6241,18 +4416,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Symfony polyfill for uuid functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.29.0" }, "funding": [ { @@ -6268,46 +4449,43 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/string", - "version": "v7.0.4", + "name": "symfony/routing", + "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + "url": "https://github.com/symfony/routing.git", + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "url": "https://api.github.com/repos/symfony/routing/zipball/858b26756ffc35a11238b269b484ee3a393a74d3", + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "symfony/translation-contracts": "<2.5" + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" }, "require-dev": { - "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" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\String\\": "" + "Symfony\\Component\\Routing\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6319,26 +4497,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "description": "Maps an HTTP request to a set of configuration variables", "homepage": "https://symfony.com", "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" + "router", + "routing", + "uri", + "url" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.4" + "source": "https://github.com/symfony/routing/tree/v7.0.3" }, "funding": [ { @@ -6354,62 +4530,44 @@ "type": "tidelift" } ], - "time": "2024-02-01T13:17:36+00:00" + "time": "2024-01-30T13:55:15+00:00" }, { - "name": "symfony/translation", - "version": "v7.0.4", + "name": "symfony/runtime", + "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0" + "url": "https://github.com/symfony/runtime.git", + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/5b75e872f7d135d7abb4613809fadc8d9f3d30a0", - "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0", + "url": "https://api.github.com/repos/symfony/runtime/zipball/ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "composer-plugin-api": "^1.0|^2.0", + "php": ">=8.2" }, "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" + "symfony/dotenv": "<6.4" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", + "composer/composer": "^2.6", "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" + "symfony/dotenv": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" }, - "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\Translation\\": "" + "Symfony\\Component\\Runtime\\": "", + "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" }, "exclude-from-classmap": [ "/Tests/" @@ -6421,18 +4579,21 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides tools to internationalize your application", + "description": "Enables decoupling PHP applications from global state", "homepage": "https://symfony.com", + "keywords": [ + "runtime" + ], "support": { - "source": "https://github.com/symfony/translation/tree/v7.0.4" + "source": "https://github.com/symfony/runtime/tree/v7.0.3" }, "funding": [ { @@ -6448,24 +4609,28 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { - "name": "symfony/translation-contracts", + "name": "symfony/service-contracts", "version": "v3.4.1", "source": { "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "06450585bf65e978026bda220cdebca3f867fde7" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", - "reference": "06450585bf65e978026bda220cdebca3f867fde7", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { @@ -6479,7 +4644,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Translation\\": "" + "Symfony\\Contracts\\Service\\": "" }, "exclude-from-classmap": [ "/Test/" @@ -6499,7 +4664,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to translation", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ "abstractions", @@ -6510,7 +4675,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" }, "funding": [ { @@ -6529,73 +4694,27 @@ "time": "2023-12-26T14:02:43+00:00" }, { - "name": "symfony/twig-bridge", - "version": "v7.0.4", + "name": "symfony/stopwatch", + "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bridge.git", - "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", - "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", + "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/translation-contracts": "^2.5|^3", - "twig/twig": "^3.0.4" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<6.4", - "symfony/form": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/mime": "<6.4", - "symfony/serializer": "<6.4", - "symfony/translation": "<6.4", - "symfony/workflow": "<6.4" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3|^4", - "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "twig/cssinliner-extra": "^2.12|^3", - "twig/inky-extra": "^2.12|^3", - "twig/markdown-extra": "^2.12|^3" + "symfony/service-contracts": "^2.5|^3" }, - "type": "symfony-bridge", + "type": "library", "autoload": { "psr-4": { - "Symfony\\Bridge\\Twig\\": "" + "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6615,10 +4734,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides integration for Twig with various Symfony components", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" + "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" }, "funding": [ { @@ -6634,52 +4753,46 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { - "name": "symfony/twig-bundle", + "name": "symfony/string", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bundle.git", - "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" + "url": "https://github.com/symfony/string.git", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", - "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", "shasum": "" }, "require": { - "composer-runtime-api": ">=2.1", "php": ">=8.2", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/framework-bundle": "<6.4", - "symfony/translation": "<6.4" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "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": "symfony-bundle", + "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Symfony\\Bundle\\TwigBundle\\": "" + "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6691,114 +4804,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "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/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-15T11:33:06+00:00" - }, - { - "name": "symfony/ux-turbo", - "version": "v2.16.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/ux-turbo.git", - "reference": "d3590a43fee73304855dfc8022ccb57b0df9f03d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/d3590a43fee73304855dfc8022ccb57b0df9f03d", - "reference": "d3590a43fee73304855dfc8022ccb57b0df9f03d", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/stimulus-bundle": "^2.9.1" - }, - "conflict": { - "symfony/flex": "<1.13" - }, - "require-dev": { - "doctrine/doctrine-bundle": "^2.4.3", - "doctrine/orm": "^2.8 | 3.0", - "phpstan/phpstan": "^1.10", - "symfony/debug-bundle": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/form": "^5.4|^6.0|^7.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/mercure-bundle": "^0.3.7", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/panther": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|6.3.*|^7.0", - "symfony/property-access": "^5.4|^6.0|^7.0", - "symfony/security-core": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "symfony/web-profiler-bundle": "^5.4|^6.0|^7.0", - "symfony/webpack-encore-bundle": "^2.1.1" - }, - "type": "symfony-bundle", - "extra": { - "thanks": { - "name": "symfony/ux", - "url": "https://github.com/symfony/ux" - } - }, - "autoload": { - "psr-4": { - "Symfony\\UX\\Turbo\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "kevin@dunglas.fr" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Hotwire Turbo integration for Symfony", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ - "hotwire", - "javascript", - "mercure", - "symfony-ux", - "turbo", - "turbo-stream" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "source": "https://github.com/symfony/ux-turbo/tree/v2.16.0" + "source": "https://github.com/symfony/string/tree/v7.0.4" }, "funding": [ { @@ -6814,62 +4839,33 @@ "type": "tidelift" } ], - "time": "2024-02-20T16:11:17+00:00" + "time": "2024-02-01T13:17:36+00:00" }, { - "name": "symfony/validator", - "version": "v7.0.4", + "name": "symfony/uid", + "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "104bc3620d0ee4091034cfbcdcf82ed727f15b7d" + "url": "https://github.com/symfony/uid.git", + "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/104bc3620d0ee4091034cfbcdcf82ed727f15b7d", - "reference": "104bc3620d0ee4091034cfbcdcf82ed727f15b7d", + "url": "https://api.github.com/repos/symfony/uid/zipball/87cedaf3fabd7b733859d4d77aa4ca598259054b", + "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php83": "^1.27", - "symfony/translation-contracts": "^2.5|^3" - }, - "conflict": { - "doctrine/lexer": "<1.1", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<7.0", - "symfony/expression-language": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/intl": "<6.4", - "symfony/property-info": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/yaml": "<6.4" - }, - "require-dev": { - "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/yaml": "^6.4|^7.0" + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Validator\\": "" + "Symfony\\Component\\Uid\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6881,18 +4877,27 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides tools to validate values", + "description": "Provides an object-oriented API to generate and represent UIDs", "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], "support": { - "source": "https://github.com/symfony/validator/tree/v7.0.4" + "source": "https://github.com/symfony/uid/tree/v7.0.3" }, "funding": [ { @@ -6908,7 +4913,7 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/var-dumper", @@ -7067,89 +5072,6 @@ ], "time": "2024-02-26T10:35:24+00:00" }, - { - "name": "symfony/web-link", - "version": "v7.0.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/web-link.git", - "reference": "855a347feb2ecfc1d1a379c739aff956d4cbec00" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/855a347feb2ecfc1d1a379c739aff956d4cbec00", - "reference": "855a347feb2ecfc1d1a379c739aff956d4cbec00", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/link": "^1.1|^2.0" - }, - "conflict": { - "symfony/http-kernel": "<6.4" - }, - "provide": { - "psr/link-implementation": "1.0|2.0" - }, - "require-dev": { - "symfony/http-kernel": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\WebLink\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Manages links between resources", - "homepage": "https://symfony.com", - "keywords": [ - "dns-prefetch", - "http", - "http2", - "link", - "performance", - "prefetch", - "preload", - "prerender", - "psr13", - "push" - ], - "support": { - "source": "https://github.com/symfony/web-link/tree/v7.0.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:02:46+00:00" - }, { "name": "symfony/yaml", "version": "v7.0.3", @@ -7221,242 +5143,40 @@ ], "time": "2024-01-23T15:02:46+00:00" }, - { - "name": "twig/extra-bundle", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "32807183753de0388c8e59f7ac2d13bb47311140" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/32807183753de0388c8e59f7ac2d13bb47311140", - "reference": "32807183753de0388c8e59f7ac2d13bb47311140", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "twig/twig": "^3.0" - }, - "require-dev": { - "league/commonmark": "^1.0|^2.0", - "symfony/phpunit-bridge": "^6.4|^7.0", - "twig/cache-extra": "^3.0", - "twig/cssinliner-extra": "^2.12|^3.0", - "twig/html-extra": "^2.12|^3.0", - "twig/inky-extra": "^2.12|^3.0", - "twig/intl-extra": "^2.12|^3.0", - "twig/markdown-extra": "^2.12|^3.0", - "twig/string-extra": "^2.12|^3.0" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Twig\\Extra\\TwigExtraBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - } - ], - "description": "A Symfony bundle for extra Twig extensions", - "homepage": "https://twig.symfony.com", - "keywords": [ - "bundle", - "extra", - "twig" - ], - "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-11-21T14:02:01+00:00" - }, - { - "name": "twig/twig", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-11-21T18:54:41+00:00" - }, { "name": "webmozart/assert", "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - } - ], - "packages-dev": [ - { - "name": "masterminds/html5", - "version": "2.8.1", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "ext-dom": "*", - "php": ">=5.3.0" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { - "Masterminds\\": "src" + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -7465,35 +5185,24 @@ ], "authors": [ { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", + "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" + "assert", + "check", + "validate" ], "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2023-05-10T11:58:31+00:00" - }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ { "name": "myclabs/deep-copy", "version": "1.11.1", @@ -9109,33 +6818,39 @@ "time": "2020-09-28T06:39:44+00:00" }, { - "name": "symfony/browser-kit", + "name": "symfony/debug-bundle", "version": "v7.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "725d5b15681685ac17b20b575254c75639722488" + "url": "https://github.com/symfony/debug-bundle.git", + "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/725d5b15681685ac17b20b575254c75639722488", - "reference": "725d5b15681685ac17b20b575254c75639722488", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/b0db5c443883ce5c10c2265c77feb9833c3d9d6d", + "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d", "shasum": "" }, "require": { + "ext-xml": "*", "php": ">=8.2", - "symfony/dom-crawler": "^6.4|^7.0" + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4" }, "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" + "symfony/config": "^6.4|^7.0", + "symfony/web-profiler-bundle": "^6.4|^7.0" }, - "type": "library", + "type": "symfony-bundle", "autoload": { "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" + "Symfony\\Bundle\\DebugBundle\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9155,10 +6870,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.0.3" + "source": "https://github.com/symfony/debug-bundle/tree/v7.0.3" }, "funding": [ { @@ -9177,53 +6892,80 @@ "time": "2024-01-23T15:02:46+00:00" }, { - "name": "symfony/css-selector", - "version": "v7.0.3", + "name": "symfony/maker-bundle", + "version": "v1.55.1", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be" + "url": "https://github.com/symfony/maker-bundle.git", + "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/ec60a4edf94e63b0556b6a0888548bb400a3a3be", - "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/11a9d3125c5b93ab4043f0f2e9927fdc55881c17", + "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17", "shasum": "" }, "require": { - "php": ">=8.2" + "doctrine/inflector": "^2.0", + "nikic/php-parser": "^4.18|^5.0", + "php": ">=8.1", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.2|^3", + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" + }, + "conflict": { + "doctrine/doctrine-bundle": "<2.10", + "doctrine/orm": "<2.15" + }, + "require-dev": { + "composer/semver": "^3.0", + "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/orm": "^2.15|^3", + "symfony/http-client": "^6.4|^7.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0|^4.x-dev" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } }, - "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Bundle\\MakerBundle\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", + "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", + "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", + "keywords": [ + "code generator", + "dev", + "generator", + "scaffold", + "scaffolding" + ], "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.0.3" + "issues": "https://github.com/symfony/maker-bundle/issues", + "source": "https://github.com/symfony/maker-bundle/tree/v1.55.1" }, "funding": [ { @@ -9239,42 +6981,49 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2024-02-21T13:41:51+00:00" }, { - "name": "symfony/debug-bundle", - "version": "v7.0.3", + "name": "symfony/phpunit-bridge", + "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/debug-bundle.git", - "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d" + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/b0db5c443883ce5c10c2265c77feb9833c3d9d6d", - "reference": "b0db5c443883ce5c10c2265c77feb9833c3d9d6d", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", "shasum": "" }, "require": { - "ext-xml": "*", - "php": ">=8.2", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "php": ">=7.2.5" }, "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4" + "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/web-profiler-bundle": "^6.4|^7.0" + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.4|^7.0", + "symfony/polyfill-php81": "^1.27" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } }, - "type": "symfony-bundle", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Bundle\\DebugBundle\\": "" + "Symfony\\Bridge\\PhpUnit\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9286,18 +7035,18 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v7.0.3" + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" }, "funding": [ { @@ -9313,35 +7062,29 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2024-02-08T19:22:56+00:00" }, { - "name": "symfony/dom-crawler", + "name": "symfony/process", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "6cb272cbec4dc7a30a853d2931766b03bea92dda" + "url": "https://github.com/symfony/process.git", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/6cb272cbec4dc7a30a853d2931766b03bea92dda", - "reference": "6cb272cbec4dc7a30a853d2931766b03bea92dda", + "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "^6.4|^7.0" + "php": ">=8.2" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9361,10 +7104,88 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Eases DOM navigation for HTML and XML documents", + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "06450585bf65e978026bda220cdebca3f867fde7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", + "reference": "06450585bf65e978026bda220cdebca3f867fde7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.0.4" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" }, "funding": [ { @@ -9380,83 +7201,99 @@ "type": "tidelift" } ], - "time": "2024-02-12T11:15:03+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { - "name": "symfony/maker-bundle", - "version": "v1.55.1", + "name": "symfony/twig-bridge", + "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/maker-bundle.git", - "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17" + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/11a9d3125c5b93ab4043f0f2e9927fdc55881c17", - "reference": "11a9d3125c5b93ab4043f0f2e9927fdc55881c17", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", "shasum": "" }, "require": { - "doctrine/inflector": "^2.0", - "nikic/php-parser": "^4.18|^5.0", - "php": ">=8.1", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "php": ">=8.2", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^3.0.4" }, "conflict": { - "doctrine/doctrine-bundle": "<2.10", - "doctrine/orm": "<2.15" + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<6.4", + "symfony/form": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.4", + "symfony/serializer": "<6.4", + "symfony/translation": "<6.4", + "symfony/workflow": "<6.4" }, "require-dev": { - "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.5.0", - "doctrine/orm": "^2.15|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/phpunit-bridge": "^6.4.1|^7.0", + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-acl": "^2.8|^3.0", "symfony/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.0|^4.x-dev" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" }, + "type": "symfony-bridge", "autoload": { "psr-4": { - "Symfony\\Bundle\\MakerBundle\\": "src/" - } + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", - "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", - "keywords": [ - "code generator", - "dev", - "generator", - "scaffold", - "scaffolding" - ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.55.1" + "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" }, "funding": [ { @@ -9472,49 +7309,52 @@ "type": "tidelift" } ], - "time": "2024-02-21T13:41:51+00:00" + "time": "2024-02-15T11:33:06+00:00" }, { - "name": "symfony/phpunit-bridge", + "name": "symfony/twig-bundle", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", - "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", "shasum": "" }, "require": { - "php": ">=7.2.5" + "composer-runtime-api": ">=2.1", + "php": ">=8.2", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" + "symfony/framework-bundle": "<6.4", + "symfony/translation": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.4|^7.0", - "symfony/polyfill-php81": "^1.27" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } + "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": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" + "Symfony\\Bundle\\TwigBundle\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9526,18 +7366,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" + "source": "https://github.com/symfony/twig-bundle/tree/v7.0.4" }, "funding": [ { @@ -9553,7 +7393,7 @@ "type": "tidelift" } ], - "time": "2024-02-08T19:22:56+00:00" + "time": "2024-02-15T11:33:06+00:00" }, { "name": "symfony/web-profiler-bundle", @@ -9685,15 +7525,89 @@ } ], "time": "2023-11-20T00:12:19+00:00" + }, + { + "name": "twig/twig", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T18:54:41+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "beberlei/metrics": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.2", + "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*" }, diff --git a/examples/config/bundles.php b/examples/config/bundles.php index f8df89b..53ad6f5 100644 --- a/examples/config/bundles.php +++ b/examples/config/bundles.php @@ -14,10 +14,7 @@ Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], - Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true], - Symfony\UX\Turbo\TurboBundle::class => ['all' => true], - Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + Beberlei\Bundle\MetricsBundle\BeberleiMetricsBundle::class => ['all' => true], ]; diff --git a/examples/config/packages/asset_mapper.yaml b/examples/config/packages/asset_mapper.yaml deleted file mode 100644 index d1ac653..0000000 --- a/examples/config/packages/asset_mapper.yaml +++ /dev/null @@ -1,5 +0,0 @@ -framework: - asset_mapper: - # The paths to make available to the asset mapper. - paths: - - assets/ diff --git a/examples/config/packages/mailer.yaml b/examples/config/packages/mailer.yaml deleted file mode 100644 index 56a650d..0000000 --- a/examples/config/packages/mailer.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - mailer: - dsn: '%env(MAILER_DSN)%' diff --git a/examples/config/packages/messenger.yaml b/examples/config/packages/messenger.yaml deleted file mode 100644 index 587083a..0000000 --- a/examples/config/packages/messenger.yaml +++ /dev/null @@ -1,24 +0,0 @@ -framework: - messenger: - failure_transport: failed - - transports: - # https://symfony.com/doc/current/messenger.html#transport-configuration - async: - dsn: '%env(MESSENGER_TRANSPORT_DSN)%' - options: - use_notify: true - check_delayed_interval: 60000 - retry_strategy: - max_retries: 3 - multiplier: 2 - failed: 'doctrine://default?queue_name=failed' - # sync: 'sync://' - - routing: - Symfony\Component\Mailer\Messenger\SendEmailMessage: async - Symfony\Component\Notifier\Message\ChatMessage: async - Symfony\Component\Notifier\Message\SmsMessage: async - - # Route your messages to the transports - # 'App\Message\YourMessage': async diff --git a/examples/config/packages/metrics.yaml b/examples/config/packages/metrics.yaml new file mode 100644 index 0000000..6b5369a --- /dev/null +++ b/examples/config/packages/metrics.yaml @@ -0,0 +1,31 @@ +beberlei_metrics: + default: logger + collectors: + influxdb_v1: + type: influxdb_v1 + host: influxdb1 + database: app + prometheus: + type: prometheus + host: prometheus + service: Prometheus\CollectorRegistry + namespace: app + graphite: + type: graphite + host: graphite + statsd: + type: statsd + host: graphite + prefix: 'app.statsd.' + dogstatsd: + type: dogstatsd + host: graphite + prefix: 'app.dogstatsd.' + dbal: + type: doctrine_dbal + logger: + type: logger + memory: + type: memory + 'null': + type: 'null' diff --git a/examples/config/packages/notifier.yaml b/examples/config/packages/notifier.yaml deleted file mode 100644 index d02f986..0000000 --- a/examples/config/packages/notifier.yaml +++ /dev/null @@ -1,12 +0,0 @@ -framework: - notifier: - chatter_transports: - texter_transports: - channel_policy: - # use chat/slack, chat/telegram, sms/twilio or sms/nexmo - urgent: ['email'] - high: ['email'] - medium: ['email'] - low: ['email'] - admin_recipients: - - { email: admin@example.com } diff --git a/examples/config/packages/security.yaml b/examples/config/packages/security.yaml deleted file mode 100644 index 367af25..0000000 --- a/examples/config/packages/security.yaml +++ /dev/null @@ -1,39 +0,0 @@ -security: - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords - password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider - providers: - users_in_memory: { memory: null } - firewalls: - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - main: - lazy: true - provider: users_in_memory - - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#the-firewall - - # https://symfony.com/doc/current/security/impersonating_user.html - # switch_user: true - - # Easy way to control access for large sections of your site - # Note: Only the *first* access control that matches will be used - access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } - -when@test: - security: - password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: - algorithm: auto - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon diff --git a/examples/config/packages/translation.yaml b/examples/config/packages/translation.yaml deleted file mode 100644 index b3f8f9c..0000000 --- a/examples/config/packages/translation.yaml +++ /dev/null @@ -1,7 +0,0 @@ -framework: - default_locale: en - translator: - default_path: '%kernel.project_dir%/translations' - fallbacks: - - en - providers: diff --git a/examples/config/packages/twig.yaml b/examples/config/packages/twig.yaml index 3f795d9..5d1e199 100644 --- a/examples/config/packages/twig.yaml +++ b/examples/config/packages/twig.yaml @@ -1,5 +1,7 @@ twig: file_name_pattern: '*.twig' + globals: + project_dir: '%kernel.project_dir%' when@test: twig: diff --git a/examples/config/packages/validator.yaml b/examples/config/packages/validator.yaml deleted file mode 100644 index dd47a6a..0000000 --- a/examples/config/packages/validator.yaml +++ /dev/null @@ -1,11 +0,0 @@ -framework: - validation: - # Enables validator auto-mapping support. - # For instance, basic validation constraints will be inferred from Doctrine's metadata. - #auto_mapping: - # App\Entity\: [] - -when@test: - framework: - validation: - not_compromised_password: false diff --git a/examples/config/routes/security.yaml b/examples/config/routes/security.yaml deleted file mode 100644 index f853be1..0000000 --- a/examples/config/routes/security.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_security_logout: - resource: security.route_loader.logout - type: service diff --git a/examples/config/services.yaml b/examples/config/services.yaml index 2d6a76f..e3d19ce 100644 --- a/examples/config/services.yaml +++ b/examples/config/services.yaml @@ -1,18 +1,10 @@ -# This file is the entry point to configure your own services. -# Files in the packages/ subdirectory configure your dependencies. - -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: services: - # default configuration for services in *this* file _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + autowire: true + autoconfigure: true - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/' exclude: @@ -20,5 +12,8 @@ services: - '../src/Entity/' - '../src/Kernel.php' - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones + Prometheus\CollectorRegistry: + arguments: + $storageAdapter: !service { class: Prometheus\Storage\APCng } + $registerDefaultMetrics: false + Prometheus\RenderTextFormat: ~ diff --git a/examples/importmap.php b/examples/importmap.php deleted file mode 100644 index 277f3f6..0000000 --- a/examples/importmap.php +++ /dev/null @@ -1,24 +0,0 @@ - [ - 'path' => './assets/app.js', - 'entrypoint' => true, - ], - '@hotwired/stimulus' => [ - 'version' => '3.2.2', - ], - '@symfony/stimulus-bundle' => [ - 'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js', - ], - '@hotwired/turbo' => [ - 'version' => '7.3.0', - ], -]; diff --git a/examples/infrastructure/docker/docker-compose.builder.yml b/examples/infrastructure/docker/docker-compose.builder.yml index f2b4807..ca6fc68 100644 --- a/examples/infrastructure/docker/docker-compose.builder.yml +++ b/examples/infrastructure/docker/docker-compose.builder.yml @@ -23,3 +23,4 @@ services: - "builder-data:/home/app" - "${COMPOSER_CACHE_DIR}:/home/app/.composer/cache" - "../..:/var/www:cached" + - "../../..:/metrics:cached" diff --git a/examples/infrastructure/docker/docker-compose.yml b/examples/infrastructure/docker/docker-compose.yml index 230a21a..7003dca 100644 --- a/examples/infrastructure/docker/docker-compose.yml +++ b/examples/infrastructure/docker/docker-compose.yml @@ -1,7 +1,13 @@ version: '3.7' volumes: + grafana-data: {} + graphite-data: {} + influxdb-data: {} postgres-data: {} + prometheus-data: {} + # redis-data: {} + # vector-data: {} services: router: @@ -19,6 +25,7 @@ services: - postgres volumes: - "../..:/var/www:cached" + - "../../..:/metrics:cached" environment: - "PHP_VERSION=${PHP_VERSION}" labels: @@ -36,3 +43,101 @@ services: - POSTGRES_PASSWORD=app volumes: - postgres-data:/var/lib/postgresql/data + + grafana: + image: grafana/grafana:latest + volumes: + - grafana-data:/var/lib/grafana + environment: + - GF_PATHS_PROVISIONING=/etc/grafana/provisioning + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_INSTALL_PLUGINS=grafana-clickhouse-datasource + entrypoint: + - sh + - -euc + - | + mkdir -p /etc/grafana/provisioning/datasources + cat < /etc/grafana/provisioning/datasources/loki.yaml + apiVersion: 1 + datasources: + - name: Loki + type: loki + access: proxy + orgId: 1 + url: http://loki:3100 + basicAuth: false + isDefault: true + version: 1 + editable: false + - name: ClickHouse + type: grafana-clickhouse-datasource + access: proxy + orgId: 1 + isDefault: false + jsonData: + host: clickhouse + port: 9000 + defaultDatabase: observability + username: observability + defaultDatasource: true + secureJsonData: + password: observability + version: 1 + editable: false + EOF + /run.sh + labels: + - "traefik.enable=true" + - "traefik.http.routers.${PROJECT_NAME}-grafana.rule=Host(`grafana.${PROJECT_ROOT_DOMAIN}`)" + - "traefik.http.routers.${PROJECT_NAME}-grafana.tls=true" + + graphite: + image: graphiteapp/graphite-statsd:1.1.10-5 + volumes: + - graphite-data:/opt/graphite/storage + expose: + - 80 + labels: + - "traefik.enable=true" + - "traefik.http.routers.${PROJECT_NAME}-graphite.rule=Host(`graphite.${PROJECT_ROOT_DOMAIN}`)" + - "traefik.http.routers.${PROJECT_NAME}-graphite.tls=true" + + influxdb1: + image: influxdb:1.8 + environment: + - INFLUXDB_DB=app + volumes: + - influxdb-data:/var/lib/influxdb + + prometheus: + image: prom/prometheus:v2.50.1 + volumes: + - ./services/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:cached + - prometheus-data:/var/lib/prometheus + + # redis: + # image: redislabs/redistimeseries:1.10.11 + # volumes: + # - "redis-data:/data" + + # redis-insight: + # image: redislabs/redisinsight:1.14.0 + # environment: + # RITRUSTEDORIGINS: https://redis.symfony-metrics.test + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.${PROJECT_NAME}-redis.rule=Host(`redis.${PROJECT_ROOT_DOMAIN}`)" + # - "traefik.http.routers.${PROJECT_NAME}-redis.tls=true" + + # vector: + # image: timberio/vector:0.36.0-debian + # volumes: + # - ./services/vector/vector.yaml:/etc/vector/vector.yaml:cached + # - vector-data:/var/lib/vector + # expose: + # - 8686 + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.${PROJECT_NAME}-vector.rule=Host(`vector.${PROJECT_ROOT_DOMAIN}`)" + # - "traefik.http.routers.${PROJECT_NAME}-vector.tls=true" diff --git a/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf b/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf index 8e939f7..64f8c39 100644 --- a/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf +++ b/examples/infrastructure/docker/services/php/frontend/etc/nginx/nginx.conf @@ -29,11 +29,6 @@ http { listen 0.0.0.0:80; root /var/www/public; - location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg)$ { - access_log off; - add_header Cache-Control "no-cache"; - } - # Remove this block if you want to access to PHP FPM monitoring # dashboard (on URL: /php-fpm-status). WARNING: on production, you must # secure this page (by user IP address, with a password, for example) diff --git a/examples/infrastructure/docker/services/prometheus/prometheus.yml b/examples/infrastructure/docker/services/prometheus/prometheus.yml new file mode 100644 index 0000000..6936b83 --- /dev/null +++ b/examples/infrastructure/docker/services/prometheus/prometheus.yml @@ -0,0 +1,14 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + +scrape_configs: + - job_name: "prometheus" + static_configs: + - targets: ["localhost:9090"] + - job_name: "app" + metrics_path: /prometheus + static_configs: + - targets: ["frontend"] diff --git a/examples/infrastructure/docker/services/vector/vector.yaml b/examples/infrastructure/docker/services/vector/vector.yaml new file mode 100644 index 0000000..13e9771 --- /dev/null +++ b/examples/infrastructure/docker/services/vector/vector.yaml @@ -0,0 +1,62 @@ +data_dir: /var/lib/vector + +api: + enabled: true + address: '0.0.0.0:8686' + +sources: + syslog: + type: syslog + mode: udp + address: '0.0.0.0:9000' + +transforms: + syslog_json: + inputs: + - syslog + type: remap + source: | + . = parse_json!(.message) + .host = get_hostname() ?? "n/a" + .environment = get_env_var("VECTOR_ENVIRONMENT") ?? "production" + .application = get_env_var("VECTOR_APPLICATION") ?? "symfony" + +sinks: + elasticsearch: + inputs: + - syslog_json + type: elasticsearch + mode: bulk + endpoints: + - 'http://elasticsearch:9200' + bulk: + index: vector-%Y.%m.%d + action: create + loki: + inputs: + - syslog_json + type: loki + encoding: + codec: json + endpoint: 'http://loki:3100' + healthcheck: false + labels: + host: '{{ host }}' + environment: '{{ environment }}' + application: '{{ application }}' + channel: '{{ channel }}' + level: '{{ level_name }}' + clickhouse: + inputs: + - syslog_json + type: clickhouse + # format: json_as_string + endpoint: 'http://clickhouse:8123' + database: observability + table: logs + healthcheck: false + auth: + strategy: basic + password: observability + user: observability + skip_unknown_fields: true diff --git a/examples/migrations/.gitignore b/examples/migrations/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/examples/migrations/Version20240302012809.php b/examples/migrations/Version20240302012809.php new file mode 100644 index 0000000..28a9ea0 --- /dev/null +++ b/examples/migrations/Version20240302012809.php @@ -0,0 +1,32 @@ +addSql('CREATE TABLE metrics (id UUID DEFAULT gen_random_uuid() NOT NULL, metric VARCHAR(255) NOT NULL, measurement INT NOT NULL, created TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); + $this->addSql('COMMENT ON COLUMN metrics.created IS \'(DC2Type:datetime_immutable)\''); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP TABLE metrics'); + } +} diff --git a/examples/phpstan.neon b/examples/phpstan.neon index 2db6d86..ff5ad2d 100644 --- a/examples/phpstan.neon +++ b/examples/phpstan.neon @@ -11,7 +11,7 @@ parameters: inferPrivatePropertyTypeFromConstructor: true symfony: - container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml' + container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml typeAliases: ContextData: ''' diff --git a/examples/src/Controller/.gitignore b/examples/src/Controller/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/examples/src/Controller/HomepageController.php b/examples/src/Controller/HomepageController.php index dcec4e1..5dfc53a 100644 --- a/examples/src/Controller/HomepageController.php +++ b/examples/src/Controller/HomepageController.php @@ -10,26 +10,55 @@ namespace App\Controller; use Beberlei\Metrics\Collector\CollectorInterface; +use Beberlei\Metrics\Collector\GaugeableCollectorInterface; +use Beberlei\Metrics\Collector\InMemory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; class HomepageController extends AbstractController { + private readonly InMemory $memoryCollector; + + /** + * @param iterable $collectors + */ public function __construct( - #[Target('null')] - private CollectorInterface $collector2, - #[Target('null2')] - private CollectorInterface $collector, + #[TaggedIterator(CollectorInterface::class)] + private readonly iterable $collectors, + #[Target('memory')] + CollectorInterface $memoryCollector, ) { + if (!$memoryCollector instanceof InMemory) { + throw new \InvalidArgumentException('The memory collector must be an instance of InMemory.'); + } + $this->memoryCollector = $memoryCollector; } #[Route('/')] public function index(): Response { + $random = date('s'); + $timing = (int) ((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000); + $gauges = random_int(0, 100); + + foreach ($this->collectors as $collector) { + $collector->measure('homepage.random', $random); + $collector->increment('homepage.visits'); + $collector->timing('homepage.duration', $timing); + if ($collector instanceof GaugeableCollectorInterface) { + $collector->gauge('homepage.gauge', $gauges); + } + } + return $this->render('homepage/index.html.twig', [ - 'controller_name' => 'HomepageController', + 'collectors' => $this->collectors, + 'random' => $this->memoryCollector->getMeasure('homepage.random'), + 'visits' => $this->memoryCollector->getMeasure('homepage.visits'), + 'timing' => $this->memoryCollector->getTiming('homepage.duration'), + 'gauge' => $this->memoryCollector->getGauge('homepage.gauge'), ]); } } diff --git a/examples/src/Controller/PrometheusController.php b/examples/src/Controller/PrometheusController.php new file mode 100644 index 0000000..586b8ff --- /dev/null +++ b/examples/src/Controller/PrometheusController.php @@ -0,0 +1,34 @@ +renderer->render($this->registry->getMetricFamilySamples()), + headers: ['Content-Type' => RenderTextFormat::MIME_TYPE], + ); + } +} diff --git a/examples/src/Entity/.gitignore b/examples/src/Entity/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/examples/src/Entity/Metrics.php b/examples/src/Entity/Metrics.php new file mode 100644 index 0000000..28fdad8 --- /dev/null +++ b/examples/src/Entity/Metrics.php @@ -0,0 +1,36 @@ + 'gen_random_uuid()'])] + public string $id; + + public function __construct( + #[ORM\Column(length: 255)] + public string $metric, + + #[ORM\Column] + public int $measurement, + + #[ORM\Column(type: Types::DATETIME_IMMUTABLE)] + public \DateTimeInterface $created, + ) { + $this->id = Uuid::v6()->toRfc4122(); + } +} diff --git a/examples/src/Repository/.gitignore b/examples/src/Repository/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/examples/src/Repository/MetricsRepository.php b/examples/src/Repository/MetricsRepository.php new file mode 100644 index 0000000..2425ec8 --- /dev/null +++ b/examples/src/Repository/MetricsRepository.php @@ -0,0 +1,30 @@ + + * + * @method Metrics|null find($id, $lockMode = null, $lockVersion = null) + * @method Metrics|null findOneBy(array $criteria, array $orderBy = null) + * @method Metrics[] findAll() + * @method Metrics[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class MetricsRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Metrics::class); + } +} diff --git a/examples/symfony.lock b/examples/symfony.lock index 2769bd4..feb3c7a 100644 --- a/examples/symfony.lock +++ b/examples/symfony.lock @@ -1,4 +1,7 @@ { + "beberlei/metrics": { + "version": "dev-massive-code-grooming" + }, "doctrine/doctrine-bundle": { "version": "2.11", "recipe": { @@ -40,21 +43,6 @@ "tests/bootstrap.php" ] }, - "symfony/asset-mapper": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "6.4", - "ref": "6c28c471640cc2c6e60812ebcb961c526ef8997f" - }, - "files": [ - "assets/app.js", - "assets/styles/app.css", - "config/packages/asset_mapper.yaml", - "importmap.php" - ] - }, "symfony/console": { "version": "7.0", "recipe": { @@ -110,18 +98,6 @@ "src/Kernel.php" ] }, - "symfony/mailer": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "4.3", - "ref": "df66ee1f226c46f01e85c29c2f7acce0596ba35a" - }, - "files": [ - "config/packages/mailer.yaml" - ] - }, "symfony/maker-bundle": { "version": "1.55", "recipe": { @@ -131,18 +107,6 @@ "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" } }, - "symfony/messenger": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "6.0", - "ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee" - }, - "files": [ - "config/packages/messenger.yaml" - ] - }, "symfony/monolog-bundle": { "version": "3.10", "recipe": { @@ -155,18 +119,6 @@ "config/packages/monolog.yaml" ] }, - "symfony/notifier": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "5.0", - "ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc" - }, - "files": [ - "config/packages/notifier.yaml" - ] - }, "symfony/phpunit-bridge": { "version": "7.0", "recipe": { @@ -195,46 +147,6 @@ "config/routes.yaml" ] }, - "symfony/security-bundle": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "6.4", - "ref": "2ae08430db28c8eb4476605894296c82a642028f" - }, - "files": [ - "config/packages/security.yaml", - "config/routes/security.yaml" - ] - }, - "symfony/stimulus-bundle": { - "version": "2.16", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "2.13", - "ref": "6acd9ff4f7fd5626d2962109bd4ebab351d43c43" - }, - "files": [ - "assets/bootstrap.js", - "assets/controllers.json", - "assets/controllers/hello_controller.js" - ] - }, - "symfony/translation": { - "version": "7.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "6.3", - "ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b" - }, - "files": [ - "config/packages/translation.yaml", - "translations/.gitignore" - ] - }, "symfony/twig-bundle": { "version": "7.0", "recipe": { @@ -248,20 +160,14 @@ "templates/base.html.twig" ] }, - "symfony/ux-turbo": { - "version": "v2.16.0" - }, - "symfony/validator": { + "symfony/uid": { "version": "7.0", "recipe": { "repo": "github.com/symfony/recipes", "branch": "main", "version": "7.0", - "ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd" - }, - "files": [ - "config/packages/validator.yaml" - ] + "ref": "0df5844274d871b37fc3816c57a768ffc60a43a5" + } }, "symfony/web-profiler-bundle": { "version": "7.0", @@ -275,8 +181,5 @@ "config/packages/web_profiler.yaml", "config/routes/web_profiler.yaml" ] - }, - "twig/extra-bundle": { - "version": "v3.8.0" } } diff --git a/examples/templates/base.html.twig b/examples/templates/base.html.twig index 3cda30f..1069c14 100644 --- a/examples/templates/base.html.twig +++ b/examples/templates/base.html.twig @@ -8,7 +8,6 @@ {% endblock %} {% block javascripts %} - {% block importmap %}{{ importmap('app') }}{% endblock %} {% endblock %} diff --git a/examples/templates/homepage/index.html.twig b/examples/templates/homepage/index.html.twig new file mode 100644 index 0000000..b55483b --- /dev/null +++ b/examples/templates/homepage/index.html.twig @@ -0,0 +1,56 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello{% endblock %} + +{% block body %} +

Metrics demo application

+ +

+ This application demonstrates how to use the + beberlei/metrics library. +
+ This controller uses all collectors defined in the + config/packages/metrics.yaml: +

    + {% for collector in collectors %} +
  • + {{ constant('class', collector) }}
  • + {% endfor %} +
+

+ +

+ The following applications let you explore the collected metrics: +

    +
  • Grafana: application with all database configureds
  • +
  • Graphite: application with graphite, statsd, dogstatsd
  • +
+

+ +

Current collectors data

+ +

Current collectors collected the following metrics:

+ + + + + + + + + + + + + + + + + + + + + + +
MesaureValue
random{{ random }}
visits{{ visits }}
timing{{ timing }}
gauge{{ gauge }}
+{% endblock %} diff --git a/examples/translations/.gitignore b/examples/translations/.gitignore deleted file mode 100644 index e69de29..0000000 From e533b9807f8b9a32ca8ed051deaca9716e01f8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 29 Feb 2024 18:27:03 +0100 Subject: [PATCH 31/32] doc: create CHANGELOG.md --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ UPGRADE-2.0.md | 17 ----------------- 2 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 UPGRADE-2.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dc71083 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,43 @@ +# CHANGELOG + +## v3.0.0 (unreleased) + +### Breaking changes + +* Drop support for PHP < 8.1 +* Drop support for Symfony < 5.4 +* Drop support for zabbix collector +* Drop support for librato collector +* Rename InfluxDB collector to InfluxDbV1 +* Change inner dependency of InfluxDbV1 +* Change inner dependency of Prometheus +* Remove the TaggableCollectorInterface. Tags can be injected in the constructor + instead + +### New features + +* collector: + * Ensure all collectors cannot raise error or exception +* bundle: + * All collectors has alias for autowiring. Use + `#[Target('name-of-the-collector')]` to inject a collector + * All collectors are tagged with `kernel.reset` to reset their state + * All collectors are tagged with + `Beberlei\Metrics\Collector\CollectorInterface` +* add a symfony application in the `examples` folder will all collectors enabled + and visualisation with Grafana + +### Minor changes + +* collector: + * Fix doctrine dbal deprecations +* chore: + * modernise PHP code, use PHP 8.1 features + * add license file, and link it in each PHP files +* ci: + * use symfony/phpunit-bridge instead of phpunit + * add php-cs-fixer + * add phpstan + * replace Travis by GitHub Actions +* composer: + * move tests to it's own folder, and it's own autoloader diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md deleted file mode 100644 index b1def08..0000000 --- a/UPGRADE-2.0.md +++ /dev/null @@ -1,17 +0,0 @@ -Upgrade from 1.x to 2.0 -======================= - -* [BC BREAK] Collector `Beberlei\Metrics\Collector\Monolog` is renamed to `Beberlei\Metrics\Collector\Logger`. - -* [BC BREAK] Collector `Beberlei\Metrics\Collector\Monolog` takes a `Psr\Log\LoggerInterface`. - -* [BC BREAK] The bundle does not rely on `Beberlei\Metrics\Factory` and `Beberlei\Metrics\Registry` anymore. - -* [BC BREAK] The bundle configuration has a new "standard" key mapping. The -`hostname` key for `Librato` is now `source`. The `hostname` key for `Zabbix` is -now `prefix`. `hostname`, and `servername` are now `host`. `serverport` is now -`port`. - -* [BC BREAK] The `Registry` is removed. - -* [BC BREAK] The `functions.php` is removed. From 4689791f4a261eb66bacd09eea7d9ad77a4eb49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sat, 2 Mar 2024 18:51:54 +0100 Subject: [PATCH 32/32] -- --- examples/composer.json | 2 +- examples/composer.lock | 1106 ++++++++++++++++------------------------ 2 files changed, 441 insertions(+), 667 deletions(-) diff --git a/examples/composer.json b/examples/composer.json index 1dd83d7..9ccc0a6 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -19,7 +19,6 @@ "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.0", "influxdb/influxdb-php": "^1.15", - "phpdocumentor/reflection-docblock": "^5.3", "phpstan/phpdoc-parser": "^1.26", "promphp/prometheus_client_php": "^2.10", "symfony/console": "7.0.*", @@ -28,6 +27,7 @@ "symfony/framework-bundle": "7.0.*", "symfony/monolog-bundle": "^3.0", "symfony/runtime": "7.0.*", + "symfony/twig-bundle": "7.0.*", "symfony/uid": "7.0.*", "symfony/yaml": "7.0.*" }, diff --git a/examples/composer.lock b/examples/composer.lock index be4b0da..91d0b4d 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8d17d049d9055c18f7e29bb59065459f", + "content-hash": "13387979a3a0d1d914b168e3a8ae7c53", "packages": [ { "name": "beberlei/metrics", @@ -12,7 +12,7 @@ "dist": { "type": "path", "url": "/metrics", - "reference": "e27a066968ab3726df32364b8afe679562672783" + "reference": "e533b9807f8b9a32ca8ed051deaca9716e01f8aa" }, "require": { "psr/log": "^1.0 || ^2.0 || ^3.0" @@ -1784,174 +1784,6 @@ ], "time": "2023-10-27T15:32:31+00:00" }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@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" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.8.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "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": "phpstan/phpdoc-parser", "version": "1.26.0", @@ -4842,33 +4674,38 @@ "time": "2024-02-01T13:17:36+00:00" }, { - "name": "symfony/uid", - "version": "v7.0.3", + "name": "symfony/translation-contracts", + "version": "v3.4.1", "source": { "type": "git", - "url": "https://github.com/symfony/uid.git", - "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "06450585bf65e978026bda220cdebca3f867fde7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/87cedaf3fabd7b733859d4d77aa4ca598259054b", - "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", + "reference": "06450585bf65e978026bda220cdebca3f867fde7", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-uuid": "^1.15" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0" + "php": ">=8.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Uid\\": "" + "Symfony\\Contracts\\Translation\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -4876,10 +4713,6 @@ "MIT" ], "authors": [ - { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4889,15 +4722,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to generate and represent UIDs", + "description": "Generic abstractions related to translation", "homepage": "https://symfony.com", "keywords": [ - "UID", - "ulid", - "uuid" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.0.3" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" }, "funding": [ { @@ -4913,47 +4749,76 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { - "name": "symfony/var-dumper", + "name": "symfony/twig-bridge", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.0" + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^3.0.4" }, "conflict": { - "symfony/console": "<6.4" + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<6.4", + "symfony/form": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.4", + "symfony/serializer": "<6.4", + "symfony/translation": "<6.4", + "symfony/workflow": "<6.4" }, "require-dev": { - "ext-iconv": "*", + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/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/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "symfony/intl": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", + "type": "symfony-bridge", "autoload": { - "files": [ - "Resources/functions/dump.php" - ], "psr-4": { - "Symfony\\Component\\VarDumper\\": "" + "Symfony\\Bridge\\Twig\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -4965,22 +4830,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" + "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" }, "funding": [ { @@ -4999,23 +4860,264 @@ "time": "2024-02-15T11:33:06+00:00" }, { - "name": "symfony/var-exporter", + "name": "symfony/twig-bundle", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", - "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", "shasum": "" }, "require": { - "php": ">=8.2" + "composer-runtime-api": ">=2.1", + "php": ">=8.2", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "twig/twig": "^3.0.4" }, - "require-dev": { + "conflict": { + "symfony/framework-bundle": "<6.4", + "symfony/translation": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bundle/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/87cedaf3fabd7b733859d4d77aa4ca598259054b", + "reference": "87cedaf3fabd7b733859d4d77aa4ca598259054b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T15:02:46+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:33:06+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", @@ -5144,62 +5246,76 @@ "time": "2024-01-23T15:02:46+00:00" }, { - "name": "webmozart/assert", - "version": "1.11.0", + "name": "twig/twig", + "version": "v3.8.0", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "url": "https://github.com/twigphp/Twig.git", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", "shasum": "" }, "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { - "phpunit/phpunit": "^8.5.13" + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, "autoload": { "psr-4": { - "Webmozart\\Assert\\": "src/" + "Twig\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" } ], - "description": "Assertions to validate method input/output with nice error messages.", + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", "keywords": [ - "assert", - "check", - "validate" + "templating" ], "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" }, - "time": "2022-06-03T18:03:27+00:00" + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T18:54:41+00:00" } ], "packages-dev": [ @@ -6916,276 +7032,56 @@ "symfony/filesystem": "^6.4|^7.0", "symfony/finder": "^6.4|^7.0", "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" - }, - "conflict": { - "doctrine/doctrine-bundle": "<2.10", - "doctrine/orm": "<2.15" - }, - "require-dev": { - "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.5.0", - "doctrine/orm": "^2.15|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/phpunit-bridge": "^6.4.1|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.0|^4.x-dev" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\MakerBundle\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", - "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", - "keywords": [ - "code generator", - "dev", - "generator", - "scaffold", - "scaffolding" - ], - "support": { - "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.55.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-21T13:41:51+00:00" - }, - { - "name": "symfony/phpunit-bridge", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", - "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.4|^7.0", - "symfony/polyfill-php81": "^1.27" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-08T19:22:56+00:00" - }, - { - "name": "symfony/process", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-22T20:27:20+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v3.4.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "06450585bf65e978026bda220cdebca3f867fde7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", - "reference": "06450585bf65e978026bda220cdebca3f867fde7", - "shasum": "" + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, - "require": { - "php": ">=8.1" + "conflict": { + "doctrine/doctrine-bundle": "<2.10", + "doctrine/orm": "<2.15" }, - "type": "library", + "require-dev": { + "composer/semver": "^3.0", + "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/orm": "^2.15|^3", + "symfony/http-client": "^6.4|^7.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0", + "symfony/security-core": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.0|^4.x-dev" + }, + "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Symfony\\Bundle\\MakerBundle\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", + "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", + "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "code generator", + "dev", + "generator", + "scaffold", + "scaffolding" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" + "issues": "https://github.com/symfony/maker-bundle/issues", + "source": "https://github.com/symfony/maker-bundle/tree/v1.55.1" }, "funding": [ { @@ -7201,76 +7097,49 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2024-02-21T13:41:51+00:00" }, { - "name": "symfony/twig-bridge", + "name": "symfony/phpunit-bridge", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bridge.git", - "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", - "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/translation-contracts": "^2.5|^3", - "twig/twig": "^3.0.4" + "php": ">=7.2.5" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<6.4", - "symfony/form": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/mime": "<6.4", - "symfony/serializer": "<6.4", - "symfony/translation": "<6.4", - "symfony/workflow": "<6.4" + "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "egulias/email-validator": "^2.1.10|^3|^4", - "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "twig/cssinliner-extra": "^2.12|^3", - "twig/inky-extra": "^2.12|^3", - "twig/markdown-extra": "^2.12|^3" + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.4|^7.0", + "symfony/polyfill-php81": "^1.27" }, + "bin": [ + "bin/simple-phpunit" + ], "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Bridge\\Twig\\": "" + "Symfony\\Bridge\\PhpUnit\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7282,18 +7151,18 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides integration for Twig with various Symfony components", + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" }, "funding": [ { @@ -7309,52 +7178,29 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-02-08T19:22:56+00:00" }, { - "name": "symfony/twig-bundle", + "name": "symfony/process", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/twig-bundle.git", - "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" + "url": "https://github.com/symfony/process.git", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", - "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", + "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", "shasum": "" }, "require": { - "composer-runtime-api": ">=2.1", - "php": ">=8.2", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "twig/twig": "^3.0.4" - }, - "conflict": { - "symfony/framework-bundle": "<6.4", - "symfony/translation": "<6.4" - }, - "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "php": ">=8.2" }, - "type": "symfony-bundle", + "type": "library", "autoload": { "psr-4": { - "Symfony\\Bundle\\TwigBundle\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7374,10 +7220,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a tight integration of Twig into the Symfony full-stack framework", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.0.4" + "source": "https://github.com/symfony/process/tree/v7.0.4" }, "funding": [ { @@ -7393,7 +7239,7 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/web-profiler-bundle", @@ -7525,78 +7371,6 @@ } ], "time": "2023-11-20T00:12:19+00:00" - }, - { - "name": "twig/twig", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-11-21T18:54:41+00:00" } ], "aliases": [],