diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..aed2bc5
--- /dev/null
+++ b/.gitea/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,23 @@
+---
+
+name: "Merge Request"
+about: "Template de MR"
+title: "[#NUMERO_TICKET] TITRE TICKET"
+ref: "main"
+
+---
+
+| Numéro du ticket | Titre du ticket |
+|------------------|-----------------|
+| | |
+
+## Description de la PR
+
+## Modification du .env
+
+## Check list
+
+- [ ] Pas de régression
+- [ ] TU/TI/TF rédigée
+- [ ] TU/TI/TF OK
+- [ ] CHANGELOG modifié
diff --git a/.gitea/workflows/auto-tag-develop.yml b/.gitea/workflows/auto-tag-develop.yml
new file mode 100644
index 0000000..5bb4ac5
--- /dev/null
+++ b/.gitea/workflows/auto-tag-develop.yml
@@ -0,0 +1,45 @@
+name: Auto Tag Develop
+
+on:
+ push:
+ branches:
+ - develop
+
+jobs:
+ tag:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.RELEASE_TOKEN }}
+ persist-credentials: true
+
+ - name: Create next tag v0.0.X
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ # Skip if current commit already has a v0.0.* tag
+ if git tag --points-at HEAD | grep -qE '^v0\.0\.'; then
+ echo "Tag already exists on this commit. Skipping."
+ exit 0
+ fi
+
+ last_tag="$(git tag -l 'v0.0.*' --sort=-v:refname | head -n1 || true)"
+ if [ -z "$last_tag" ]; then
+ next_tag="v0.0.1"
+ else
+ patch="${last_tag##v0.0.}"
+ if ! [[ "$patch" =~ ^[0-9]+$ ]]; then
+ echo "Unexpected tag format: $last_tag" >&2
+ exit 1
+ fi
+ next_tag="v0.0.$((patch + 1))"
+ fi
+
+ git config user.name "gitea-actions"
+ git config user.email "gitea-actions@local"
+ git tag "$next_tag"
+ git push origin "$next_tag"
diff --git a/.gitea/workflows/release-artefact.yml b/.gitea/workflows/release-artefact.yml
new file mode 100644
index 0000000..6abf770
--- /dev/null
+++ b/.gitea/workflows/release-artefact.yml
@@ -0,0 +1,43 @@
+name: Build Release Artefact
+
+on:
+ push:
+ tags:
+ - "v0.0.*"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "8.4"
+ extensions: mbstring, intl, xml, curl, zip
+
+ - name: Install backend deps (prod)
+ env:
+ APP_ENV: prod
+ APP_DEBUG: "0"
+ run: composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
+
+ - name: Build artefact
+ shell: bash
+ run: |
+ set -euo pipefail
+ mkdir -p release
+ tar --exclude=.git --exclude=.gitea -czf "release/ednotif-bundle-${GITHUB_REF_NAME}.tar.gz" \
+ .
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v2
+ with:
+ files: release/ednotif-bundle-${{ github.ref_name }}.tar.gz
+ env:
+ GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
diff --git a/.idea/Soap-bundle.iml b/.idea/Soap-bundle.iml
index 1bdaf5d..6dd76b1 100644
--- a/.idea/Soap-bundle.iml
+++ b/.idea/Soap-bundle.iml
@@ -56,6 +56,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/php.xml b/.idea/php.xml
index 437b4a7..58c2b13 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -12,57 +12,77 @@
-
+
-
-
-
+
+
-
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..04b76c2
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,56 @@
+in('src')
+ ->notName('Kernel.php')
+;
+
+$rules = [
+ '@Symfony' => true,
+ '@PSR12' => true,
+ '@PHP84Migration' => true,
+ '@PER-CS' => true,
+ '@PhpCsFixer' => true,
+ 'strict_param' => true,
+ 'strict_comparison' => true,
+ 'no_useless_else' => true,
+ 'no_useless_return' => true,
+ 'binary_operator_spaces' => [
+ 'operators' => [
+ '=' => 'align_single_space_minimal',
+ '||' => 'align_single_space_minimal',
+ '=>' => 'align_single_space_minimal',
+ ],
+ ],
+ 'global_namespace_import' => [
+ 'import_classes' => true,
+ 'import_constants' => true,
+ 'import_functions' => true,
+ ],
+ 'modernize_strpos' => true, // needs PHP 8+ or polyfill
+ 'no_superfluous_phpdoc_tags' => true,
+ 'echo_tag_syntax' => true,
+ 'semicolon_after_instruction' => true,
+ 'combine_consecutive_unsets' => true,
+ 'ternary_to_null_coalescing' => true,
+ 'declare_strict_types' => true,
+ 'operator_linebreak' => [
+ 'position' => 'beginning',
+ ],
+ 'no_unused_imports' => true,
+ 'single_line_throw' => false,
+ 'php_unit_test_class_requires_covers' => false,
+];
+
+$config = new Config();
+
+return $config
+ ->setRiskyAllowed(true)
+ ->setRules($rules)
+ ->setFinder($finder)
+;
diff --git a/commit-msg b/commit-msg
new file mode 100644
index 0000000..901d964
--- /dev/null
+++ b/commit-msg
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+MSG_FILE="${1}"
+FIRST_LINE="$(head -n 1 "$MSG_FILE" | tr -d '\r')"
+
+# Autoriser commits auto-générés par git
+if [[ "$FIRST_LINE" =~ ^Merge\ ]]; then
+ exit 0
+fi
+
+# Types autorisés (MINUSCULES uniquement)
+# Optionnel: scope => feat(auth) : ...
+REGEX='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z0-9._-]+\))?\ :\ .+'
+
+if [[ ! "$FIRST_LINE" =~ $REGEX ]]; then
+ echo "❌ Message de commit invalide."
+ echo ""
+ echo "➡️ Format attendu : () : "
+ echo "➡️ Types autorisés (minuscules uniquement) :"
+ echo " build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test"
+ echo ""
+ echo "✅ Exemples :"
+ echo " feat : add login page"
+ echo " fix(auth) : prevent null token crash"
+ echo " docs : update README"
+ echo ""
+ echo "❌ Exemple refusé :"
+ echo " Feat : add login page"
+ exit 1
+fi
diff --git a/composer.json b/composer.json
index c1f5d86..14184d5 100644
--- a/composer.json
+++ b/composer.json
@@ -1,5 +1,5 @@
{
- "name": "Malio/ednotif-bundle",
+ "name": "malio/ednotif-bundle",
"description": "Client EDNOTIF (Guichet + wsIpBNotif) pour Symfony",
"type": "symfony-bundle",
"version": "0.0.1",
@@ -27,6 +27,7 @@
}
},
"require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.92",
"phpunit/phpunit": "^12.0",
"symfony/phpunit-bridge": "^8.0"
},
diff --git a/composer.lock b/composer.lock
index ec7a773..e8c381a 100644
--- a/composer.lock
+++ b/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": "2ea08ae20540bc012297beb885d74903",
+ "content-hash": "2a9c3edf53b3ae827236eb3a2dc64cc8",
"packages": [
{
"name": "psr/cache",
@@ -1969,6 +1969,504 @@
}
],
"packages-dev": [
+ {
+ "name": "clue/ndjson-react",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/reactphp-ndjson.git",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/stream": "^1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35",
+ "react/event-loop": "^1.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\React\\NDJson\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
+ "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.",
+ "homepage": "https://github.com/clue/reactphp-ndjson",
+ "keywords": [
+ "NDJSON",
+ "json",
+ "jsonlines",
+ "newline",
+ "reactphp",
+ "streaming"
+ ],
+ "support": {
+ "issues": "https://github.com/clue/reactphp-ndjson/issues",
+ "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-23T10:58:28+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "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.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-12T16:29:46+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-20T19:15:30+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "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",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "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": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-06T16:37:16+00:00"
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9 || ^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Evenement\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "support": {
+ "issues": "https://github.com/igorw/evenement/issues",
+ "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+ },
+ "time": "2023-08-08T05:53:35+00:00"
+ },
+ {
+ "name": "fidry/cpu-core-counter",
+ "version": "1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-deprecation-rules": "^2.0.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
+ "keywords": [
+ "CPU",
+ "core"
+ ],
+ "support": {
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-14T07:29:31+00:00"
+ },
+ {
+ "name": "friendsofphp/php-cs-fixer",
+ "version": "v3.92.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
+ "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
+ "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
+ "shasum": ""
+ },
+ "require": {
+ "clue/ndjson-react": "^1.3",
+ "composer/semver": "^3.4",
+ "composer/xdebug-handler": "^3.0.5",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "fidry/cpu-core-counter": "^1.3",
+ "php": "^7.4 || ^8.0",
+ "react/child-process": "^0.6.6",
+ "react/event-loop": "^1.5",
+ "react/socket": "^1.16",
+ "react/stream": "^1.4",
+ "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
+ "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.33",
+ "symfony/polyfill-php80": "^1.33",
+ "symfony/polyfill-php81": "^1.33",
+ "symfony/polyfill-php84": "^1.33",
+ "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0",
+ "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "facile-it/paraunit": "^1.3.1 || ^2.7",
+ "infection/infection": "^0.31",
+ "justinrainbow/json-schema": "^6.6",
+ "keradus/cli-executor": "^2.3",
+ "mikey179/vfsstream": "^1.6.12",
+ "php-coveralls/php-coveralls": "^2.9",
+ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
+ "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
+ "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46",
+ "symfony/polyfill-php85": "^1.33",
+ "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
+ "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.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/"
+ },
+ "exclude-from-classmap": [
+ "src/**/Internal/"
+ ]
+ },
+ "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.92.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/keradus",
+ "type": "github"
+ }
+ ],
+ "time": "2026-01-08T21:57:37+00:00"
+ },
{
"name": "myclabs/deep-copy",
"version": "1.13.4",
@@ -2644,6 +3142,532 @@
],
"time": "2026-01-16T16:28:10+00:00"
},
+ {
+ "name": "react/cache",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "^3.0 || ^2.0 || ^1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/cache/issues",
+ "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-11-30T15:59:55+00:00"
+ },
+ {
+ "name": "react/child-process",
+ "version": "v0.6.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/child-process.git",
+ "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3",
+ "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/event-loop": "^1.2",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/socket": "^1.16",
+ "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\ChildProcess\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven library for executing child processes with ReactPHP.",
+ "keywords": [
+ "event-driven",
+ "process",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/child-process/issues",
+ "source": "https://github.com/reactphp/child-process/tree/v0.6.7"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-12-23T15:25:20+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v1.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "^1.0 || ^0.6 || ^0.5",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3 || ^2",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/dns/issues",
+ "source": "https://github.com/reactphp/dns/tree/v1.14.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-18T19:34:28+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "suggest": {
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/event-loop/issues",
+ "source": "https://github.com/reactphp/event-loop/tree/v1.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-17T20:46:25+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "1.12.28 || 1.4.10",
+ "phpunit/phpunit": "^9.6 || ^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v3.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-08-19T18:57:03+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v1.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^1.13",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3.3 || ^2",
+ "react/promise-stream": "^1.4",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/socket/issues",
+ "source": "https://github.com/reactphp/socket/tree/v1.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-19T20:47:34+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.2"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/stream/issues",
+ "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-11T12:45:25+00:00"
+ },
{
"name": "sebastian/cli-parser",
"version": "4.2.0",
@@ -3593,6 +4617,96 @@
],
"time": "2024-10-20T05:08:20+00:00"
},
+ {
+ "name": "symfony/console",
+ "version": "v8.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
+ "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.4|^8.0"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^7.4|^8.0",
+ "symfony/dependency-injection": "^7.4|^8.0",
+ "symfony/event-dispatcher": "^7.4|^8.0",
+ "symfony/http-foundation": "^7.4|^8.0",
+ "symfony/http-kernel": "^7.4|^8.0",
+ "symfony/lock": "^7.4|^8.0",
+ "symfony/messenger": "^7.4|^8.0",
+ "symfony/process": "^7.4|^8.0",
+ "symfony/stopwatch": "^7.4|^8.0",
+ "symfony/var-dumper": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v8.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-23T14:52:06+00:00"
+ },
{
"name": "symfony/phpunit-bridge",
"version": "v8.0.3",
@@ -3678,6 +4792,638 @@
],
"time": "2025-12-10T13:10:54+00:00"
},
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-27T09:58:17+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-02T08:10:11+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php81",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
+ "reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-24T13:30:11+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v8.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "0cbbd88ec836f8757641c651bb995335846abb78"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/0cbbd88ec836f8757641c651bb995335846abb78",
+ "reference": "0cbbd88ec836f8757641c651bb995335846abb78",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4"
+ },
+ "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/v8.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-19T10:01:18+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/67df1914c6ccd2d7b52f70d40cf2aea02159d942",
+ "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "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/v8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:36:47+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v8.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc",
+ "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "symfony/polyfill-ctype": "^1.8",
+ "symfony/polyfill-intl-grapheme": "^1.33",
+ "symfony/polyfill-intl-normalizer": "^1.0",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.4|^8.0",
+ "symfony/http-client": "^7.4|^8.0",
+ "symfony/intl": "^7.4|^8.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v8.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-01T09:13:36+00:00"
+ },
{
"name": "theseer/tokenizer",
"version": "2.0.1",
diff --git a/config/services.php b/config/services.php
index 26a8a8b..5802cdb 100644
--- a/config/services.php
+++ b/config/services.php
@@ -2,31 +2,37 @@
declare(strict_types=1);
-use Malio\EdnotifBundle\Api\BovinApi;
-use Malio\EdnotifBundle\Api\BovinApiInterface;
use Malio\EdnotifBundle\Auth\TokenProvider;
-use Malio\EdnotifBundle\Soap\SoapClientFactory;
+use Malio\EdnotifBundle\Bovin\Api\BovinApi;
+use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
+use Malio\EdnotifBundle\Bovin\Mapper\AnimalFileMapper;
+use Malio\EdnotifBundle\Shared\Soap\SoapClientFactory;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
return static function (ContainerConfigurator $container): void {
$services = $container->services()
->defaults()
->autowire()
- ->autoconfigure();
+ ->autoconfigure()
+ ;
$services->set(SoapClientFactory::class)
- ->arg('$soapOptions', '%ednotif.soap_options%');
+ ->arg('$soapOptions', '%ednotif.soap_options%')
+ ;
- // SoapClient Guichet
$services->set('ednotif.soap.guichet', SoapClient::class)
->factory([service(SoapClientFactory::class), 'create'])
- ->args(['%ednotif.guichet_wsdl%']);
+ ->args(['%ednotif.guichet_wsdl%'])
+ ;
- // SoapClient Métier
- $services->set('ednotif.soap.metier', SoapClient::class)
+ $services->set('ednotif.soap.business', SoapClient::class)
->factory([service(SoapClientFactory::class), 'create'])
- ->args(['%ednotif.metier_wsdl%']);
+ ->args(['%ednotif.metier_wsdl%'])
+ ;
+
+ $services->set(AnimalFileMapper::class);
$services->set(TokenProvider::class)
->args([
@@ -38,13 +44,18 @@ return static function (ContainerConfigurator $container): void {
'%ednotif.password%',
'%ednotif.token_ttl_seconds%',
service('cache.app'),
- ]);
+ ])
+ ;
$services->set(BovinApi::class)
->args([
service(TokenProvider::class),
- service('ednotif.soap.metier'),
- ]);
+ service('ednotif.soap.business'),
+ service(AnimalFileMapper::class),
+ '%ednotif.exploitation_country_code%',
+ '%ednotif.exploitation_number%',
+ ])
+ ;
$services->alias(BovinApiInterface::class, BovinApi::class)->public();
};
diff --git a/docker-compose.yml b/docker-compose.yml
index 69ed830..71400a7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,8 +6,12 @@ services:
dockerfile: Dockerfile
args:
DOCKER_PHP_VERSION: ${DOCKER_PHP_VERSION}
+ CURRENT_UID: ${CURRENT_UID}
+ CURRENT_GID: ${CURRENT_GID}
environment:
PHP_IDE_CONFIG: serverName=${DOCKER_APP_NAME}-docker
+ XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal}
+ XDEBUG_CONFIG: client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} client_port=9003
extra_hosts:
- "host.docker.internal:${CLIENT_HOST:-0.0.0.0}"
volumes:
diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile
index 08b140c..9f5d7e4 100644
--- a/docker/php/Dockerfile
+++ b/docker/php/Dockerfile
@@ -14,12 +14,26 @@ RUN apt-get update && apt-get install -y \
&& docker-php-ext-install soap \
&& docker-php-ext-install dom
-# Install Composer
-COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+# installation de composer
+RUN rm -rf /var/cache/apk/* && rm -rf /tmp/* && \
+ curl --insecure https://getcomposer.org/composer.phar -o /usr/bin/composer && chmod +x /usr/bin/composer
# install Symfony Flex in the CI environment
RUN composer global config --no-plugins allow-plugins.symfony/flex true
RUN composer global require --no-progress --no-scripts --no-plugins symfony/flex
-# Set working directory
-WORKDIR /app
+###> User ###
+ARG CURRENT_UID
+ARG CURRENT_GID
+# mapping du user host avec www-data
+RUN usermod -o -u ${CURRENT_UID} www-data && groupmod -o -g ${CURRENT_GID} www-data
+RUN chown www-data:www-data -R /var/www/*
+RUN chown www-data:www-data -R /var/www/.*
+###< User ###
+
+RUN rm -rf \
+ /var/lib/apt/lists/* \
+ /tmp/* \
+ /var/tmp/*
+
+WORKDIR /app
\ No newline at end of file
diff --git a/makefile b/makefile
index 200b98c..e1db21b 100644
--- a/makefile
+++ b/makefile
@@ -40,11 +40,17 @@ restart: env-init
$(DOCKER_COMPOSE) down
CURRENT_UID=$(shell id -u) CURRENT_GID=$(shell id -g) $(DOCKER_COMPOSE) up -d
-install: composer-install
+install: copy-git-hook composer-install
# Supprime tout est réinstalle tout (Attention ça supprime la bdd aussi)
reset: delete_built_dir remove_orphans build-without-cache start wait install
+copy-git-hook:
+ $(EXEC_PHP) cp pre-commit .git/hooks/
+ $(EXEC_PHP) cp commit-msg .git/hooks/
+ $(EXEC_PHP) chmod a+x .git/hooks/pre-commit
+ $(EXEC_PHP) chmod a+x .git/hooks/commit-msg
+
composer-install:
$(EXEC_PHP) composer install
@@ -67,5 +73,17 @@ build-without-cache:
shell:
$(EXEC_PHP_INTERACTIVE) bash
+shell-root:
+ $(EXEC_PHP_INTERACTIVE_ROOT) bash
+
+# Lance php fixer
+php-cs-fixer-all:
+ $(EXEC_PHP) php vendor/bin/php-cs-fixer fix
+
+# Utilisé par le pre-commit pour fix les fichiers modifiés
+php-cs-fixer-allow-risky:
+ @echo "Fixing files: $(FILES)"
+ $(EXEC_PHP_CS_FIXER) fix --config=.php-cs-fixer.dist.php --allow-risky=yes $(FILES)
+
wait:
sleep 10
\ No newline at end of file
diff --git a/pre-commit b/pre-commit
new file mode 100644
index 0000000..3270079
--- /dev/null
+++ b/pre-commit
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+echo "######### Pre-commit hook start #############"
+echo "--- php-cs-fixer pre commit hook start ---"
+
+FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.php$')
+# Vérifier s'il y a des fichiers PHP modifiés
+if [ -n "$FILES" ]; then
+ echo "Running PHP CS Fixer on staged PHP files..."
+
+ # Convertir la liste des fichiers en une chaîne séparée par des espaces
+ FILES_LIST=""
+ for FILE in $FILES; do
+ FILES_LIST="$FILES_LIST $FILE"
+ done
+
+ # Exécuter la cible make pour PHP CS Fixer
+ make php-cs-fixer-allow-risky FILES="$FILES_LIST"
+
+ # Ajouter les fichiers corrigés au commit
+ git add $FILES
+else
+ echo "No PHP files to fix."
+fi
+echo "--- php-cs-fixer pre commit hook finish---"
+
+echo "All checks passed. Proceeding with commit."
+exit 0
diff --git a/src/Api/BovinApi.php b/src/Api/BovinApi.php
deleted file mode 100644
index 8f4853b..0000000
--- a/src/Api/BovinApi.php
+++ /dev/null
@@ -1,112 +0,0 @@
-tokenProvider->getToken();
-
- $payload = [[
- 'JetonAuthentification' => $token,
- 'Exploitation' => [
- 'CodePays' => $codePays,
- 'NumeroExploitation' => $exploitationNumero,
- ],
- 'Bovin' => [
- 'CodePays' => $codePays,
- 'NumeroNational' => $numeroNational,
- ],
- ]];
-
- try {
- /** @var object $response */
- $response = $this->metierClient->__soapCall('IpBGetDossierAnimal', $payload);
- } catch (SoapFault $e) {
- // Si c’est un souci de jeton, tu peux invalider et retenter une fois (optionnel)
- throw new \RuntimeException('SOAP Fault lors de IpBGetDossierAnimal: ' . $e->getMessage(), 0, $e);
- }
-
- $rs = $response->ReponseStandard ?? null;
- $ok = is_object($rs) && (($rs->Resultat ?? false) === true);
-
- if (!$ok) {
- $anom = $rs->Anomalie ?? null;
- $code = (string)($anom->Code ?? 'UNKNOWN');
- $sev = (int)($anom->Severite ?? 1);
- $msg = (string)($anom->Message ?? 'Appel EDNOTIF refusé');
- throw new EdnotifException($code, $sev, $msg);
- }
-
- $identite = [];
- $periodes = [];
-
- $bovinNode = $response->ReponseSpecifique->Bovin ?? null;
- if (is_object($bovinNode)) {
- $identiteObj = $bovinNode->IdentiteBovin ?? null;
- if (is_object($identiteObj)) {
- $identite = $this->objectToArray($identiteObj);
- }
-
- $pp = $bovinNode->PeriodesPresences->PeriodePresence ?? null;
- foreach ($this->normalizeList($pp) as $periode) {
- if (!is_object($periode)) {
- continue;
- }
- $entree = is_object($periode->Entree ?? null) ? $this->objectToArray($periode->Entree) : [];
- $sortie = is_object($periode->Sortie ?? null) ? $this->objectToArray($periode->Sortie) : null;
-
- $row = ['entree' => $entree];
- if ($sortie !== null) {
- $row['sortie'] = $sortie;
- }
- $periodes[] = $row;
- }
- }
-
- return new DossierAnimalDto(
- numeroNational: $numeroNational,
- identiteBovin: $identite,
- periodesPresence: $periodes,
- rawResponse: $response,
- );
- }
-
- /**
- * @return list
- */
- private function normalizeList(mixed $value): array
- {
- if ($value === null) {
- return [];
- }
- if (is_array($value)) {
- return $value;
- }
- return [$value];
- }
-
- /**
- * @return array
- */
- private function objectToArray(object $obj): array
- {
- // conversion simple (suffisante pour démarrer)
- return json_decode(json_encode($obj, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR);
- }
-}
diff --git a/src/Api/BovinApiInterface.php b/src/Api/BovinApiInterface.php
deleted file mode 100644
index 68df8e8..0000000
--- a/src/Api/BovinApiInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-getCacheKey();
- $item = $this->cachePool->getItem($cacheKey);
+ $item = $this->cachePool->getItem($cacheKey);
if ($item->isHit()) {
$token = $item->get();
- if (is_string($token) && $token !== '') {
+ if (is_string($token) && '' !== $token) {
return $token;
}
}
@@ -44,6 +48,9 @@ final class TokenProvider
return $token;
}
+ /**
+ * @throws InvalidArgumentException
+ */
public function invalidateToken(): void
{
$this->cachePool->deleteItem($this->getCacheKey());
@@ -52,16 +59,16 @@ final class TokenProvider
private function createToken(): string
{
$profil = array_filter([
- 'Entreprise' => $this->entreprise,
- 'Zone' => $this->zone,
+ 'Entreprise' => $this->entreprise,
+ 'Zone' => $this->zone,
'Application' => $this->application,
- ], static fn ($v) => $v !== null && $v !== '');
+ ], static fn ($v) => null !== $v && '' !== $v);
$payload = [
'Identification' => [
- 'UserId' => $this->login,
+ 'UserId' => $this->login,
'Password' => $this->password,
- 'Profil' => $profil,
+ 'Profil' => $profil,
],
];
@@ -69,7 +76,7 @@ final class TokenProvider
/** @var object $response */
$response = $this->guichetClient->__soapCall('tkCreateIdentification', [$payload]);
} catch (SoapFault $e) {
- throw new \RuntimeException('SOAP Fault lors de tkCreateIdentification: ' . $e->getMessage(), 0, $e);
+ throw new RuntimeException('SOAP Fault lors de tkCreateIdentification: '.$e->getMessage(), 0, $e);
}
$rs = $response->ReponseStandard ?? null;
@@ -77,15 +84,16 @@ final class TokenProvider
if (!$ok) {
$anom = $rs->Anomalie ?? null;
- $code = (string)($anom->Code ?? 'UNKNOWN');
- $sev = (int)($anom->Severite ?? 1);
- $msg = (string)($anom->Message ?? 'Authentification refusée');
+ $code = (string) ($anom->Code ?? 'UNKNOWN');
+ $sev = (int) ($anom->Severite ?? 1);
+ $msg = (string) ($anom->Message ?? 'Authentification refusée');
+
throw new EdnotifException($code, $sev, $msg);
}
$token = $response->Jeton ?? null;
- if (!is_string($token) || $token === '') {
- throw new \RuntimeException('Guichet: réponse OK mais Jeton absent.');
+ if (!is_string($token) || '' === $token) {
+ throw new RuntimeException('Guichet: réponse OK mais Jeton absent.');
}
return $token;
@@ -93,6 +101,6 @@ final class TokenProvider
private function getCacheKey(): string
{
- return 'ednotif.token.' . hash('sha256', $this->entreprise . '|' . $this->login);
+ return 'ednotif.token.'.hash('sha256', $this->entreprise.'|'.$this->login);
}
}
diff --git a/src/Bovin/Api/BovinApi.php b/src/Bovin/Api/BovinApi.php
new file mode 100644
index 0000000..9b9ff67
--- /dev/null
+++ b/src/Bovin/Api/BovinApi.php
@@ -0,0 +1,64 @@
+tokenProvider->getToken();
+
+ $requestPayload = [[
+ 'JetonAuthentification' => $token,
+ 'Exploitation' => [
+ 'CodePays' => $this->exploitationCountryCode,
+ 'NumeroExploitation' => $this->exploitationNumber,
+ ],
+ 'Bovin' => [
+ 'CodePays' => $countryCode,
+ 'NumeroNational' => $nationalNumber,
+ ],
+ ]];
+
+ try {
+ /** @var object $soapResponse */
+ $soapResponse = $this->businessClient->__soapCall('IpBGetDossierAnimal', $requestPayload);
+ } catch (SoapFault $soapFault) {
+ throw new RuntimeException('SOAP Fault on IpBGetDossierAnimal: '.$soapFault->getMessage(), 0, $soapFault);
+ }
+
+ // Throw uniquement si Resultat=false (erreur métier)
+ $standardResponseNode = $soapResponse->ReponseStandard ?? null;
+ $isOk = is_object($standardResponseNode) && (($standardResponseNode->Resultat ?? false) === true);
+
+ if (!$isOk) {
+ $anomalyNode = is_object($standardResponseNode) ? ($standardResponseNode->Anomalie ?? null) : null;
+
+ throw new EdnotifException(
+ codeAnomalie: (string) ($anomalyNode->Code ?? 'UNKNOWN'),
+ severite: (int) ($anomalyNode->Severite ?? 1),
+ message: (string) ($anomalyNode->Message ?? 'EDNOTIF error')
+ );
+ }
+
+ return $this->bovinDossierMapper->map($soapResponse);
+ }
+}
diff --git a/src/Bovin/Api/BovinApiInterface.php b/src/Bovin/Api/BovinApiInterface.php
new file mode 100644
index 0000000..f162fda
--- /dev/null
+++ b/src/Bovin/Api/BovinApiInterface.php
@@ -0,0 +1,12 @@
+ $presencePeriods
+ */
+ public function __construct(
+ public StandardResponseDto $standardResponse,
+ public ?BovinIdentificationDto $identification,
+ public array $presencePeriods,
+ public ?object $rawSoapResponse, // pour garder 100% des data
+ ) {}
+}
diff --git a/src/Bovin/Dto/BovinIdentificationDto.php b/src/Bovin/Dto/BovinIdentificationDto.php
new file mode 100644
index 0000000..4e9b48c
--- /dev/null
+++ b/src/Bovin/Dto/BovinIdentificationDto.php
@@ -0,0 +1,20 @@
+ $identiteBovin
+ * @param array $identiteBovin
* @param list, sortie?: array}> $periodesPresence
*/
public function __construct(
@@ -15,6 +15,5 @@ final readonly class DossierAnimalDto
public array $identiteBovin,
public array $periodesPresence,
public object $rawResponse,
- ) {
- }
+ ) {}
}
diff --git a/src/Bovin/Dto/ExploitationRef.php b/src/Bovin/Dto/ExploitationRef.php
new file mode 100644
index 0000000..710c8f8
--- /dev/null
+++ b/src/Bovin/Dto/ExploitationRef.php
@@ -0,0 +1,13 @@
+mapStandardResponse($soapResponse->ReponseStandard ?? null);
+
+ $specificResponseNode = $soapResponse->ReponseSpecifique ?? null;
+ $bovinNode = is_object($specificResponseNode) ? ($specificResponseNode->Bovin ?? null) : null;
+
+ $identification = null;
+ $presencePeriods = [];
+
+ if (is_object($bovinNode)) {
+ $identificationNode = $bovinNode->IdentiteBovin ?? null;
+ if (is_object($identificationNode)) {
+ $identification = $this->mapIdentification($identificationNode);
+ }
+
+ $presencePeriodsNode = $bovinNode->PeriodesPresences->PeriodePresence ?? null;
+ foreach ($this->normalizeToList($presencePeriodsNode) as $presencePeriodNode) {
+ if (!is_object($presencePeriodNode)) {
+ continue;
+ }
+ $presencePeriods[] = $this->mapPresencePeriod($presencePeriodNode);
+ }
+ }
+
+ return new AnimalFileDto(
+ standardResponse: $standardResponse,
+ identification: $identification,
+ presencePeriods: $presencePeriods,
+ rawSoapResponse: $soapResponse
+ );
+ }
+
+ private function mapStandardResponse(mixed $standardResponseNode): StandardResponseDto
+ {
+ $result = (bool) ($standardResponseNode->Resultat ?? false);
+
+ $anomalyNode = $standardResponseNode->Anomalie ?? null;
+ $anomaly = null;
+
+ if (is_object($anomalyNode)) {
+ $anomaly = new AnomalyDto(
+ code: $this->toNullableString($anomalyNode->Code ?? null),
+ severity: $this->toNullableInt($anomalyNode->Severite ?? null),
+ message: $this->toNullableString($anomalyNode->Message ?? null),
+ );
+ }
+
+ return new StandardResponseDto($result, $anomaly);
+ }
+
+ private function mapIdentification(object $identificationNode): BovinIdentificationDto
+ {
+ $bovinRef = $this->mapBovinRef($identificationNode->Bovin ?? null);
+
+ $birthDate = null;
+ $birthDateNode = $identificationNode->DateNaissance ?? null;
+ if (is_object($birthDateNode)) {
+ $birthDate = new DateValueDto(
+ date: $this->toNullableDate($birthDateNode->Date ?? null),
+ completenessFlag: $this->toNullableString($birthDateNode->TemoinCompletude ?? null),
+ );
+ }
+
+ $motherCarrier = $this->mapParentInfo($identificationNode->MerePorteuse ?? null);
+ $fatherIpg = $this->mapParentInfo($identificationNode->PereIPG ?? null);
+ $birthExploitation = $this->mapExploitationRef($identificationNode->ExploitationNaissance ?? null);
+
+ return new BovinIdentificationDto(
+ bovin: $bovinRef,
+ sex: $this->toNullableString($identificationNode->Sexe ?? null),
+ breedType: $this->toNullableString($identificationNode->TypeRacial ?? null),
+ birthDate: $birthDate,
+ workNumber: $this->toNullableString($identificationNode->NumeroTravail ?? null),
+ isFilie: $this->toNullableBool($identificationNode->StatutFilie ?? null),
+ motherCarrier: $motherCarrier,
+ fatherIpg: $fatherIpg,
+ birthExploitation: $birthExploitation,
+ );
+ }
+
+ private function mapPresencePeriod(object $presencePeriodNode): PresencePeriodDto
+ {
+ $entryNode = $presencePeriodNode->Entree ?? null;
+ $exitNode = $presencePeriodNode->Sortie ?? null;
+
+ $entryMovement = is_object($entryNode) ? $this->mapMovement($entryNode, 'entry') : null;
+ $exitMovement = is_object($exitNode) ? $this->mapMovement($exitNode, 'exit') : null;
+
+ return new PresencePeriodDto(
+ entry: $entryMovement,
+ exit: $exitMovement,
+ );
+ }
+
+ private function mapMovement(object $movementNode, string $direction): MovementDto
+ {
+ $dateValue = null;
+ $causeValue = null;
+
+ if ('entry' === $direction) {
+ // SOAP: DateEntree / CauseEntree
+ $dateValue = $movementNode->DateEntree ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
+ $causeValue = $movementNode->CauseEntree ?? null;
+ } else {
+ // SOAP (souvent): DateSortie / CauseSortie
+ $dateValue = $movementNode->DateSortie ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
+ $causeValue = $movementNode->CauseSortie ?? null;
+ }
+
+ $exploitationRef = $this->mapExploitationRef($movementNode->Exploitation ?? null);
+
+ return new MovementDto(
+ date: $this->toNullableDate($dateValue),
+ cause: $this->toNullableString($causeValue),
+ exploitation: $exploitationRef,
+ );
+ }
+
+ private function mapParentInfo(mixed $parentNode): ?ParentInfoDto
+ {
+ if (!is_object($parentNode)) {
+ return null;
+ }
+
+ $bovinRef = $this->mapBovinRef($parentNode->Bovin ?? null);
+
+ return new ParentInfoDto(
+ bovin: $bovinRef,
+ breedType: $this->toNullableString($parentNode->TypeRacial ?? null),
+ );
+ }
+
+ private function mapBovinRef(mixed $bovinNode): ?BovinRef
+ {
+ if (!is_object($bovinNode)) {
+ return null;
+ }
+
+ return new BovinRef(
+ countryCode: $this->toNullableString($bovinNode->CodePays ?? null),
+ nationalNumber: $this->toNullableString($bovinNode->NumeroNational ?? null),
+ );
+ }
+
+ private function mapExploitationRef(mixed $exploitationNode): ?ExploitationRef
+ {
+ if (!is_object($exploitationNode)) {
+ return null;
+ }
+
+ return new ExploitationRef(
+ countryCode: $this->toNullableString($exploitationNode->CodePays ?? null),
+ exploitationNumber: $this->toNullableString($exploitationNode->NumeroExploitation ?? null),
+ );
+ }
+
+ /** @return list */
+ private function normalizeToList(mixed $value): array
+ {
+ if (null === $value) {
+ return [];
+ }
+
+ return is_array($value) ? $value : [$value];
+ }
+
+ private function toNullableString(mixed $value): ?string
+ {
+ if (null === $value) {
+ return null;
+ }
+ $stringValue = trim((string) $value);
+
+ return '' === $stringValue ? null : $stringValue;
+ }
+
+ private function toNullableInt(mixed $value): ?int
+ {
+ if (null === $value) {
+ return null;
+ }
+ if (is_int($value)) {
+ return $value;
+ }
+ if (is_numeric($value)) {
+ return (int) $value;
+ }
+
+ return null;
+ }
+
+ private function toNullableBool(mixed $value): ?bool
+ {
+ if (null === $value) {
+ return null;
+ }
+
+ return (bool) $value;
+ }
+
+ private function toNullableDate(mixed $value): ?DateTimeImmutable
+ {
+ if (!is_string($value) || '' === trim($value)) {
+ return null;
+ }
+
+ try {
+ return new DateTimeImmutable($value);
+ } catch (Throwable) {
+ return null;
+ }
+ }
+}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 8665e59..b404ebc 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -7,6 +7,9 @@ namespace Malio\EdnotifBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
+use const SOAP_SINGLE_ELEMENT_ARRAYS;
+use const WSDL_CACHE_BOTH;
+
final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
@@ -26,6 +29,9 @@ final class Configuration implements ConfigurationInterface
->scalarNode('login')->cannotBeEmpty()->isRequired()->end()
->scalarNode('password')->cannotBeEmpty()->isRequired()->end()
+ ->scalarNode('exploitation_number')->cannotBeEmpty()->isRequired()->end()
+ ->scalarNode('exploitation_country_code')->defaultValue('FR')->end()
+
->integerNode('token_ttl_seconds')->min(30)->defaultValue(900)->end()
->arrayNode('soap_options')
@@ -34,11 +40,12 @@ final class Configuration implements ConfigurationInterface
->booleanNode('trace')->defaultFalse()->end()
->booleanNode('exceptions')->defaultTrue()->end()
->integerNode('connection_timeout')->min(1)->defaultValue(15)->end()
- ->integerNode('cache_wsdl')->defaultValue(\WSDL_CACHE_BOTH)->end()
- ->integerNode('features')->defaultValue(\SOAP_SINGLE_ELEMENT_ARRAYS)->end()
+ ->integerNode('cache_wsdl')->defaultValue(WSDL_CACHE_BOTH)->end()
+ ->integerNode('features')->defaultValue(SOAP_SINGLE_ELEMENT_ARRAYS)->end()
->end()
->end()
- ->end();
+ ->end()
+ ;
return $treeBuilder;
}
diff --git a/src/DependencyInjection/EdnotifExtension.php b/src/DependencyInjection/EdnotifExtension.php
index a78f0e8..f908104 100644
--- a/src/DependencyInjection/EdnotifExtension.php
+++ b/src/DependencyInjection/EdnotifExtension.php
@@ -14,6 +14,7 @@ final class EdnotifExtension extends Extension
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
+
/** @var array{
* guichet_wsdl:string,
* metier_wsdl:string,
@@ -35,13 +36,16 @@ final class EdnotifExtension extends Extension
$container->setParameter('ednotif.zone', $config['zone']);
$container->setParameter('ednotif.application', $config['application']);
+ $container->setParameter('ednotif.exploitation_number', $config['exploitation_number']);
+ $container->setParameter('ednotif.exploitation_country_code', $config['exploitation_country_code']);
+
$container->setParameter('ednotif.login', $config['login']);
$container->setParameter('ednotif.password', $config['password']);
$container->setParameter('ednotif.token_ttl_seconds', $config['token_ttl_seconds']);
$container->setParameter('ednotif.soap_options', $config['soap_options']);
- $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
+ $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config'));
$loader->load('services.php');
}
}
diff --git a/src/EdnotifBundle.php b/src/EdnotifBundle.php
index f3d3679..d965220 100644
--- a/src/EdnotifBundle.php
+++ b/src/EdnotifBundle.php
@@ -6,6 +6,4 @@ namespace Malio\EdnotifBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
-final class EdnotifBundle extends Bundle
-{
-}
+final class EdnotifBundle extends Bundle {}
diff --git a/src/Shared/Dto/AnomalyDto.php b/src/Shared/Dto/AnomalyDto.php
new file mode 100644
index 0000000..78acad5
--- /dev/null
+++ b/src/Shared/Dto/AnomalyDto.php
@@ -0,0 +1,14 @@
+ $soapOptions
*/
- public function __construct(private array $soapOptions = [])
- {
- }
+ public function __construct(private array $soapOptions = []) {}
public function create(string $wsdl): SoapClient
{