diff --git a/.github/workflows/Deploy.yml b/.github/workflows/Deploy.yml
index 1b7cb84..604a101 100644
--- a/.github/workflows/Deploy.yml
+++ b/.github/workflows/Deploy.yml
@@ -3,28 +3,36 @@ name: Deploy
on:
push:
branches:
- - master
+ - '*'
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
+
+ - name: Log in to the Container registry
+ uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- - name: Cache Composer packages
- id: composer-cache
- uses: actions/cache@v2
- with:
- path: vendor
- key: ${{ runner.os }}-node-${{ hashFiles('**/composer.lock') }}
- restore-keys: |
- ${{ runner.os }}-node-
+ - name: Extract metadata for Docker
+ id: metadata
+ uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- - name: Build and push Docker images
- uses: docker/build-push-action@v1
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
- repository: pmsipilot/docker-compose-viz
- tag_with_ref: true
+ - name: Build and push Docker image
+ uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/.gitignore b/.gitignore
index 13b7173..cf2ac55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
-vendor/
-docker.lock
bin/
+coverage/
+vendor/
+
!bin/dcv
!bin/entrypoint.sh
-.cache/
-.php_cs.cache
+.php-cs-fixer.cache
+.phpunit.result.cache
+docker.lock
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
new file mode 100644
index 0000000..2009b85
--- /dev/null
+++ b/.php-cs-fixer.php
@@ -0,0 +1,16 @@
+in(__DIR__ . '/src')
+ ->in(__DIR__ . '/tests')
+;
+
+$rules = [
+ '@PSR12' => true,
+ 'strict_param' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+];
+
+return (new PhpCsFixer\Config())->setRules($rules)
+ ->setFinder($finder)
+;
\ No newline at end of file
diff --git a/.php_cs b/.php_cs
deleted file mode 100644
index b92f2ea..0000000
--- a/.php_cs
+++ /dev/null
@@ -1,18 +0,0 @@
-in(__DIR__.DIRECTORY_SEPARATOR.'src')
- ->in(__DIR__.DIRECTORY_SEPARATOR.'spec')
-;
-
-return (new PhpCsFixer\Config())
- ->setRules([
- '@PSR2' => true,
- '@Symfony' => true,
- 'array_syntax' => ['syntax' => 'short'],
- 'no_useless_else' => true,
- 'no_useless_return' => true,
- 'ordered_class_elements' => true,
- ])
- ->setFinder($finder)
-;
diff --git a/Dockerfile b/Dockerfile
index 0762663..6584b51 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM php:7.4-alpine as builder
+FROM php:8.1-alpine as builder
COPY composer.json /dcv/composer.json
COPY composer.lock /dcv/composer.lock
@@ -10,7 +10,7 @@ RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" &&
php -r "unlink('composer-setup.php');" && \
php composer.phar install --prefer-dist
-FROM php:7.4-alpine
+FROM php:8.1-alpine
RUN apk update && \
apk add graphviz ttf-dejavu && \
@@ -18,8 +18,8 @@ RUN apk update && \
/var/cache/apk/* \
/tmp/*
-COPY bin/ /dcv/bin
-COPY src/ /dcv/src
+COPY bin/ /dcv/bin/
+COPY src/ /dcv/src/
COPY --from=builder /dcv/vendor /dcv/vendor
RUN chmod +x /dcv/bin/dcv
diff --git a/Makefile b/Makefile
index 43e5d69..8bf3a98 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,8 @@ docker: docker.lock
test: vendor unit cs
+vendor: vendor/composer/installed.json
+
unit: vendor
$(COMPOSER) run ut
@@ -24,10 +26,10 @@ clean:
rm -rf vendor/
docker.lock: Dockerfile bin/entrypoint.sh vendor src/application.php src/functions.php
- $(DOCKER) build -t $(DCV_IMAGE_NAME) .
+ $(DOCKER) build -t $(DCV_IMAGE_NAME) --no-cache .
touch docker.lock
-vendor: composer.lock
+vendor/composer/installed.json: composer.lock
$(COMPOSER) install --prefer-dist
composer.lock: composer.json
diff --git a/bin/dcv b/bin/dcv
index 21e20f0..678f68c 100755
--- a/bin/dcv
+++ b/bin/dcv
@@ -14,7 +14,7 @@ require_once resolve(
]
);
-require_once resolve(
+$application = require_once resolve(
[
__DIR__,
'..',
@@ -22,4 +22,6 @@ require_once resolve(
'application.php',
]
);
+
+$application->run();
?>
diff --git a/composer.json b/composer.json
index 7f5a992..46ae6de 100644
--- a/composer.json
+++ b/composer.json
@@ -2,15 +2,16 @@
"name": "pmsipilot/docker-compose-viz",
"description": "Docker compose graph visualization",
"require": {
- "php": "^7.2",
- "symfony/yaml": "^3.1 || ^4",
- "symfony/console": "^3.1",
+ "php": "^8.1",
+ "symfony/yaml": "^5.4",
+ "symfony/console": "^5.4",
"clue/graph": "^0.9",
"graphp/graphviz": "^0.2"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2",
- "kahlan/kahlan": "^4.7"
+ "friendsofphp/php-cs-fixer": "^v3.12.0",
+ "kahlan/kahlan": "*",
+ "phpunit/phpunit": "^9.5"
},
"license": "MIT",
"authors": [
@@ -20,14 +21,27 @@
}
],
"autoload": {
- "files": ["src/functions.php"],
+ "files": [
+ "src/config.php",
+ "src/functions.php",
+ "src/network.php",
+ "src/port.php",
+ "src/secret.php",
+ "src/service.php",
+ "src/volume.php"
+ ],
"psr-4": {
"PMSIpilot\\DockerComposeViz\\": "src/"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "PMSIpilot\\DockerComposeViz\\Tests\\": "tests/"
+ }
+ },
"scripts": {
- "cs": "php-cs-fixer fix",
- "cst": "php-cs-fixer fix --dry-run",
- "ut": "kahlan --grep='*.php' --reporter=verbose --persistent=false"
+ "cs": "php-cs-fixer fix --allow-risky=yes",
+ "cst": "php-cs-fixer fix --dry-run --allow-risky=yes",
+ "ut": "phpunit tests --testdox --color"
}
}
diff --git a/composer.lock b/composer.lock
index 3f1ad57..91cc970 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,27 +4,27 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "883183cc51537213776e61c66e969e5a",
+ "content-hash": "fd2f8848bec593d31804276cd059005c",
"packages": [
{
"name": "clue/graph",
- "version": "v0.9.1",
+ "version": "v0.9.3",
"source": {
"type": "git",
"url": "https://github.com/graphp/graph.git",
- "reference": "07ce1e2f6d5be2ff600ce13660b25f25cf928c66"
+ "reference": "d1661c0a0e011a8550fa60ae5354f230d1555909"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/graphp/graph/zipball/07ce1e2f6d5be2ff600ce13660b25f25cf928c66",
- "reference": "07ce1e2f6d5be2ff600ce13660b25f25cf928c66",
+ "url": "https://api.github.com/repos/graphp/graph/zipball/d1661c0a0e011a8550fa60ae5354f230d1555909",
+ "reference": "d1661c0a0e011a8550fa60ae5354f230d1555909",
"shasum": ""
},
"require": {
- "php": "^7.0 || ^5.3"
+ "php": ">=5.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"suggest": {
"graphp/algorithms": "Common graph algorithms, such as Dijkstra and Moore-Bellman-Ford (shortest path), minimum spanning tree (MST), Kruskal, Prim and many more..",
@@ -40,6 +40,12 @@
"license": [
"MIT"
],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
"description": "GraPHP is the mathematical graph/network library written in PHP.",
"homepage": "https://github.com/graphp/graph",
"keywords": [
@@ -49,7 +55,21 @@
"network",
"vertex"
],
- "time": "2019-10-02T09:10:26+00:00"
+ "support": {
+ "issues": "https://github.com/graphp/graph/issues",
+ "source": "https://github.com/graphp/graph/tree/v0.9.3"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-30T09:22:01+00:00"
},
{
"name": "graphp/graphviz",
@@ -91,34 +111,38 @@
"graphp",
"graphviz"
],
+ "support": {
+ "issues": "https://github.com/graphp/graphviz/issues",
+ "source": "https://github.com/graphp/graphviz/tree/v0.2.2"
+ },
"time": "2019-10-04T13:30:55+00:00"
},
{
- "name": "psr/log",
- "version": "1.1.3",
+ "name": "psr/container",
+ "version": "2.0.2",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/log.git",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=7.4.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1.x-dev"
+ "dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "Psr\\Log\\": "Psr/Log/"
+ "Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -128,51 +152,66 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interface for logging libraries",
- "homepage": "https://github.com/php-fig/log",
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
"keywords": [
- "log",
- "psr",
- "psr-3"
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
],
- "time": "2020-03-23T09:12:05+00:00"
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
},
{
"name": "symfony/console",
- "version": "v3.4.42",
+ "version": "v5.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13"
+ "reference": "984ea2c0f45f42dfed01d2f3987b187467c4b16d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13",
- "reference": "bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13",
+ "url": "https://api.github.com/repos/symfony/console/zipball/984ea2c0f45f42dfed01d2f3987b187467c4b16d",
+ "reference": "984ea2c0f45f42dfed01d2f3987b187467c4b16d",
"shasum": ""
},
"require": {
- "php": "^5.5.9|>=7.0.8",
- "symfony/debug": "~2.8|~3.0|~4.0",
- "symfony/polyfill-mbstring": "~1.0"
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.1|^6.0"
},
"conflict": {
- "symfony/dependency-injection": "<3.4",
- "symfony/process": "<3.3"
+ "psr/log": ">=3",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
+ "symfony/lock": "<4.4",
+ "symfony/process": "<4.4"
},
"provide": {
- "psr/log-implementation": "1.0"
+ "psr/log-implementation": "1.0|2.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "~3.3|~4.0",
- "symfony/dependency-injection": "~3.4|~4.0",
- "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
- "symfony/lock": "~3.4|~4.0",
- "symfony/process": "~3.3|~4.0"
+ "psr/log": "^1|^2",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^4.4|^5.0|^6.0",
+ "symfony/lock": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
@@ -181,11 +220,6 @@
"symfony/process": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
@@ -208,47 +242,63 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Console Component",
+ "description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
- "time": "2020-05-30T18:58:05+00:00"
+ "keywords": [
+ "cli",
+ "command line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.4.14"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-07T08:01:20+00:00"
},
{
- "name": "symfony/debug",
- "version": "v4.4.10",
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.1.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/debug.git",
- "reference": "28f92d08bb6d1fddf8158e02c194ad43870007e6"
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/28f92d08bb6d1fddf8158e02c194ad43870007e6",
- "reference": "28f92d08bb6d1fddf8158e02c194ad43870007e6",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
+ "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
- "psr/log": "~1.0",
- "symfony/polyfill-php80": "^1.15"
- },
- "conflict": {
- "symfony/http-kernel": "<3.4"
- },
- "require-dev": {
- "symfony/http-kernel": "^3.4|^4.0|^5.0"
+ "php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-main": "3.1-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Component\\Debug\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
+ "files": [
+ "function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -257,34 +307,54 @@
],
"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 Debug Component",
+ "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
- "time": "2020-05-24T08:33:35+00:00"
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-25T11:15:52+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.18.0",
+ "version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
+ "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
- "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+ "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
@@ -292,7 +362,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -300,12 +370,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -329,32 +399,49 @@
"polyfill",
"portable"
],
- "time": "2020-07-14T12:35:20+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-24T11:49:31+00:00"
},
{
- "name": "symfony/polyfill-mbstring",
- "version": "v1.18.0",
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "433d05519ce6990bf3530fba6957499d327395c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
+ "reference": "433d05519ce6990bf3530fba6957499d327395c2",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
- "ext-mbstring": "For best performance"
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -362,12 +449,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -383,38 +470,59 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for the Mbstring extension",
+ "description": "Symfony polyfill for intl's grapheme_* functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
- "mbstring",
+ "grapheme",
+ "intl",
"polyfill",
"portable",
"shim"
],
- "time": "2020-07-14T12:35:20+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-24T11:49:31+00:00"
},
{
- "name": "symfony/polyfill-php80",
- "version": "v1.18.0",
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
+ "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": ""
},
"require": {
- "php": ">=7.0.8"
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -422,12 +530,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -437,10 +545,6 @@
"MIT"
],
"authors": [
- {
- "name": "Ion Bazan",
- "email": "ion.bazan@gmail.com"
- },
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
@@ -450,56 +554,75 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
+ "intl",
+ "normalizer",
"polyfill",
"portable",
"shim"
],
- "time": "2020-07-14T12:35:20+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-24T11:49:31+00:00"
},
{
- "name": "symfony/yaml",
- "version": "v4.4.10",
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "c2d2cc66e892322cfcc03f8f12f8340dbd7a3f8a"
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/c2d2cc66e892322cfcc03f8f12f8340dbd7a3f8a",
- "reference": "c2d2cc66e892322cfcc03f8f12f8340dbd7a3f8a",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
+ "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
- "symfony/polyfill-ctype": "~1.8"
- },
- "conflict": {
- "symfony/console": "<3.4"
+ "php": ">=7.1"
},
- "require-dev": {
- "symfony/console": "^3.4|^4.0|^5.0"
+ "provide": {
+ "ext-mbstring": "*"
},
"suggest": {
- "symfony/console": "For validating YAML files using the lint command"
+ "ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-main": "1.26-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
"psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -507,50 +630,79 @@
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Yaml Component",
+ "description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
- "time": "2020-05-20T08:37:50+00:00"
- }
- ],
- "packages-dev": [
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-24T11:49:31+00:00"
+ },
{
- "name": "composer/semver",
- "version": "1.5.1",
+ "name": "symfony/polyfill-php73",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/composer/semver.git",
- "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de"
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
- "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.5 || ^5.0.5"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.x-dev"
+ "dev-main": "1.26-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
"psr-4": {
- "Composer\\Semver\\": "src"
- }
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -558,417 +710,2513 @@
],
"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": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "Rob Bast",
- "email": "rob.bast@gmail.com",
- "homepage": "http://robbast.nl"
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
"keywords": [
- "semantic",
- "semver",
- "validation",
- "versioning"
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
],
- "time": "2020-01-13T12:06:48+00:00"
+ "time": "2022-05-24T11:49:31+00:00"
},
{
- "name": "composer/xdebug-handler",
- "version": "1.4.2",
+ "name": "symfony/polyfill-php80",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/composer/xdebug-handler.git",
- "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51"
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
- "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0 || ^8.0",
- "psr/log": "^1.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
+ "php": ">=7.1"
},
"type": "library",
- "autoload": {
- "psr-4": {
- "Composer\\XdebugHandler\\": "src"
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.26-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
}
},
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\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.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-10T07:21:04+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239",
+ "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^2.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.1-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.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-30T19:18:58+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v6.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "7e7e0ff180d4c5a6636eaad57b65092014b61864"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/7e7e0ff180d4c5a6636eaad57b65092014b61864",
+ "reference": "7e7e0ff180d4c5a6636eaad57b65092014b61864",
+ "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.0"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/translation-contracts": "^2.0|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0"
+ },
+ "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.1.6"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-10T09:34:31+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v5.4.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "e83fe9a72011f07c662da46a05603d66deeeb487"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/e83fe9a72011f07c662da46a05603d66deeeb487",
+ "reference": "e83fe9a72011f07c662da46a05603d66deeeb487",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/console": "<5.3"
+ },
+ "require-dev": {
+ "symfony/console": "^5.3|^6.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "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/v5.4.14"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-03T15:15:50+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "composer/pcre",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd",
+ "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd",
+ "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.0.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": "2022-02-25T20:21:48+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9",
+ "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9",
+ "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": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/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": "2022-04-01T19:23:25+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": "doctrine/annotations",
+ "version": "1.13.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/annotations.git",
+ "reference": "648b0343343565c4a056bfc8392201385e8d89f0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0",
+ "reference": "648b0343343565c4a056bfc8392201385e8d89f0",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "1.*",
+ "ext-tokenizer": "*",
+ "php": "^7.1 || ^8.0",
+ "psr/cache": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "doctrine/cache": "^1.11 || ^2.0",
+ "doctrine/coding-standard": "^6.0 || ^8.1",
+ "phpstan/phpstan": "^1.4.10 || ^1.8.0",
+ "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5",
+ "symfony/cache": "^4.4 || ^5.2",
+ "vimeo/psalm": "^4.10"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "Docblock Annotations Parser",
+ "homepage": "https://www.doctrine-project.org/projects/annotations.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "parser"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/annotations/issues",
+ "source": "https://github.com/doctrine/annotations/tree/1.13.3"
+ },
+ "time": "2022-07-02T10:48:51+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.22"
+ },
+ "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/1.4.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%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T08:28:38+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229",
+ "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9.0",
+ "phpstan/phpstan": "^1.3",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+ }
+ },
+ "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/1.2.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%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-28T11:07:21+00:00"
+ },
+ {
+ "name": "friendsofphp/php-cs-fixer",
+ "version": "v3.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
+ "reference": "eae11d945e2885d86e1c080eec1bb30a2aa27998"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/eae11d945e2885d86e1c080eec1bb30a2aa27998",
+ "reference": "eae11d945e2885d86e1c080eec1bb30a2aa27998",
+ "shasum": ""
+ },
+ "require": {
+ "composer/semver": "^3.2",
+ "composer/xdebug-handler": "^3.0.3",
+ "doctrine/annotations": "^1.13",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0",
+ "sebastian/diff": "^4.0",
+ "symfony/console": "^5.4 || ^6.0",
+ "symfony/event-dispatcher": "^5.4 || ^6.0",
+ "symfony/filesystem": "^5.4 || ^6.0",
+ "symfony/finder": "^5.4 || ^6.0",
+ "symfony/options-resolver": "^5.4 || ^6.0",
+ "symfony/polyfill-mbstring": "^1.23",
+ "symfony/polyfill-php80": "^1.25",
+ "symfony/polyfill-php81": "^1.25",
+ "symfony/process": "^5.4 || ^6.0",
+ "symfony/stopwatch": "^5.4 || ^6.0"
+ },
+ "require-dev": {
+ "justinrainbow/json-schema": "^5.2",
+ "keradus/cli-executor": "^1.5",
+ "mikey179/vfsstream": "^1.6.10",
+ "php-coveralls/php-coveralls": "^2.5.2",
+ "php-cs-fixer/accessible-object": "^1.1",
+ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2",
+ "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1",
+ "phpspec/prophecy": "^1.15",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.5",
+ "phpunitgoodpractices/polyfill": "^1.6",
+ "phpunitgoodpractices/traits": "^1.9.2",
+ "symfony/phpunit-bridge": "^6.0",
+ "symfony/yaml": "^5.4 || ^6.0"
+ },
+ "suggest": {
+ "ext-dom": "For handling output formats in XML",
+ "ext-mbstring": "For handling non-UTF8 characters."
+ },
+ "bin": [
+ "php-cs-fixer"
+ ],
+ "type": "application",
+ "autoload": {
+ "psr-4": {
+ "PhpCsFixer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Dariusz Rumiński",
+ "email": "dariusz.ruminski@gmail.com"
+ }
+ ],
+ "description": "A tool to automatically fix PHP code style",
+ "support": {
+ "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues",
+ "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.12.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/keradus",
+ "type": "github"
+ }
+ ],
+ "time": "2022-10-12T14:20:51+00:00"
+ },
+ {
+ "name": "kahlan/kahlan",
+ "version": "5.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kahlan/kahlan.git",
+ "reference": "9edb4daeba5494bcd38a125c5d960447998902f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kahlan/kahlan/zipball/9edb4daeba5494bcd38a125c5d960447998902f4",
+ "reference": "9edb4daeba5494bcd38a125c5d960447998902f4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "bin": [
+ "bin/kahlan"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Kahlan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "CrysaLEAD"
+ }
+ ],
+ "description": "The PHP Test Framework for Freedom, Truth and Justice.",
+ "keywords": [
+ "BDD",
+ "Behavior-Driven Development",
+ "Monkey Patching",
+ "TDD",
+ "mock",
+ "stub",
+ "testing",
+ "unit test"
+ ],
+ "support": {
+ "issues": "https://github.com/kahlan/kahlan/issues",
+ "source": "https://github.com/kahlan/kahlan/tree/5.2.1"
+ },
+ "time": "2022-06-18T14:06:54+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "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.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T13:19:32+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.15.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-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/v4.15.1"
+ },
+ "time": "2022-09-04T07:30:47+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.18",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/12fddc491826940cf9b7e88ad9664cf51f0f6d0a",
+ "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.14",
+ "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": "*",
+ "ext-xdebug": "*"
+ },
+ "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",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.18"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-10-27T13:35:33+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.5.26",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2",
+ "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.3.1",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.1",
+ "phar-io/manifest": "^2.0.3",
+ "phar-io/version": "^3.0.2",
+ "php": ">=7.3",
+ "phpunit/php-code-coverage": "^9.2.13",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.3",
+ "phpunit/php-timer": "^5.0.2",
+ "sebastian/cli-parser": "^1.0.1",
+ "sebastian/code-unit": "^1.0.6",
+ "sebastian/comparator": "^4.0.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": "*",
+ "ext-xdebug": "*"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.5-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",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26"
+ },
+ "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": "2022-10-28T06:00:21+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/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": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376",
+ "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.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/2.0.0"
+ },
+ "time": "2021-07-14T16:41:46+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.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.7",
+ "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.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:52:27+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "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": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "John Stevenson",
- "email": "john-stevenson@blueyonder.co.uk"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
}
],
- "description": "Restarts a process without Xdebug.",
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
- "Xdebug",
- "performance"
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2020-06-04T11:16:35+00:00"
+ "time": "2020-10-26T13:10:38+00:00"
},
{
- "name": "doctrine/annotations",
- "version": "1.10.3",
+ "name": "sebastian/environment",
+ "version": "5.1.4",
"source": {
"type": "git",
- "url": "https://github.com/doctrine/annotations.git",
- "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d"
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/5db60a4969eba0e0c197a19c077780aadbc43c5d",
- "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"shasum": ""
},
"require": {
- "doctrine/lexer": "1.*",
- "ext-tokenizer": "*",
- "php": "^7.1 || ^8.0"
+ "php": ">=7.3"
},
"require-dev": {
- "doctrine/cache": "1.*",
- "phpunit/phpunit": "^7.5"
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9.x-dev"
+ "dev-master": "5.1-dev"
}
},
"autoload": {
- "psr-4": {
- "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+ "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.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-04-03T09:37:03+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": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Guilherme Blanco",
- "email": "guilhermeblanco@gmail.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
},
{
- "name": "Roman Borschel",
- "email": "roman@code-factory.org"
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
},
{
- "name": "Benjamin Eberlei",
- "email": "kontakt@beberlei.de"
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
},
{
- "name": "Jonathan Wage",
- "email": "jonwage@gmail.com"
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
},
{
- "name": "Johannes Schmitt",
- "email": "schmittjoh@gmail.com"
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
}
],
- "description": "Docblock Annotations Parser",
- "homepage": "http://www.doctrine-project.org",
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
- "annotations",
- "docblock",
- "parser"
+ "export",
+ "exporter"
],
- "time": "2020-05-25T17:24:27+00:00"
+ "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": "doctrine/lexer",
- "version": "1.2.1",
+ "name": "sebastian/global-state",
+ "version": "5.0.5",
"source": {
"type": "git",
- "url": "https://github.com/doctrine/lexer.git",
- "reference": "e864bbf5904cb8f5bb334f99209b48018522f042"
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042",
- "reference": "e864bbf5904cb8f5bb334f99209b48018522f042",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "doctrine/coding-standard": "^6.0",
- "phpstan/phpstan": "^0.11.8",
- "phpunit/phpunit": "^8.2"
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "5.0-dev"
}
},
"autoload": {
- "psr-4": {
- "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Guilherme Blanco",
- "email": "guilhermeblanco@gmail.com"
- },
- {
- "name": "Roman Borschel",
- "email": "roman@code-factory.org"
- },
- {
- "name": "Johannes Schmitt",
- "email": "schmittjoh@gmail.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
}
],
- "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",
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
- "annotations",
- "docblock",
- "lexer",
- "parser",
- "php"
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2020-05-25T17:44:05+00:00"
+ "time": "2022-02-14T08:28:10+00:00"
},
{
- "name": "friendsofphp/php-cs-fixer",
- "version": "v2.16.4",
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.3",
"source": {
"type": "git",
- "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
- "reference": "1023c3458137ab052f6ff1e09621a721bfdeca13"
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/1023c3458137ab052f6ff1e09621a721bfdeca13",
- "reference": "1023c3458137ab052f6ff1e09621a721bfdeca13",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
"shasum": ""
},
"require": {
- "composer/semver": "^1.4",
- "composer/xdebug-handler": "^1.2",
- "doctrine/annotations": "^1.2",
- "ext-json": "*",
- "ext-tokenizer": "*",
- "php": "^5.6 || ^7.0",
- "php-cs-fixer/diff": "^1.3",
- "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
- "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0",
- "symfony/filesystem": "^3.0 || ^4.0 || ^5.0",
- "symfony/finder": "^3.0 || ^4.0 || ^5.0",
- "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0",
- "symfony/polyfill-php70": "^1.0",
- "symfony/polyfill-php72": "^1.4",
- "symfony/process": "^3.0 || ^4.0 || ^5.0",
- "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0"
+ "nikic/php-parser": "^4.6",
+ "php": ">=7.3"
},
"require-dev": {
- "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0",
- "justinrainbow/json-schema": "^5.0",
- "keradus/cli-executor": "^1.2",
- "mikey179/vfsstream": "^1.6",
- "php-coveralls/php-coveralls": "^2.1",
- "php-cs-fixer/accessible-object": "^1.0",
- "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1",
- "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1",
- "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1",
- "phpunitgoodpractices/traits": "^1.8",
- "symfony/phpunit-bridge": "^5.1",
- "symfony/yaml": "^3.0 || ^4.0 || ^5.0"
+ "phpunit/phpunit": "^9.3"
},
- "suggest": {
- "ext-dom": "For handling output formats in XML",
- "ext-mbstring": "For handling non-UTF8 characters.",
- "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.",
- "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.",
- "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible."
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
},
- "bin": [
- "php-cs-fixer"
- ],
- "type": "application",
"autoload": {
- "psr-4": {
- "PhpCsFixer\\": "src/"
- },
"classmap": [
- "tests/Test/AbstractFixerTestCase.php",
- "tests/Test/AbstractIntegrationCaseFactory.php",
- "tests/Test/AbstractIntegrationTestCase.php",
- "tests/Test/Assert/AssertTokensTrait.php",
- "tests/Test/IntegrationCase.php",
- "tests/Test/IntegrationCaseFactory.php",
- "tests/Test/IntegrationCaseFactoryInterface.php",
- "tests/Test/InternalIntegrationCaseFactory.php",
- "tests/Test/IsIdenticalConstraint.php",
- "tests/TestCase.php"
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
+ "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.3"
+ },
+ "funding": [
{
- "name": "Dariusz Rumiński",
- "email": "dariusz.ruminski@gmail.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "A tool to automatically fix PHP code style",
- "time": "2020-06-27T23:57:46+00:00"
+ "time": "2020-11-28T06:42:11+00:00"
},
{
- "name": "kahlan/kahlan",
- "version": "4.7.5",
+ "name": "sebastian/object-enumerator",
+ "version": "4.0.4",
"source": {
"type": "git",
- "url": "https://github.com/kahlan/kahlan.git",
- "reference": "c529ef24201053ba76d3c8c3531acd76b629ce87"
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/kahlan/kahlan/zipball/c529ef24201053ba76d3c8c3531acd76b629ce87",
- "reference": "c529ef24201053ba76d3c8c3531acd76b629ce87",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
"shasum": ""
},
"require": {
- "php": ">=5.5"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "squizlabs/php_codesniffer": "^3.4"
+ "phpunit/phpunit": "^9.3"
},
- "bin": [
- "bin/kahlan"
- ],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
"autoload": {
- "psr-4": {
- "Kahlan\\": "src/"
- },
- "files": [
- "src/functions.php"
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "CrysaLEAD"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
}
],
- "description": "The PHP Test Framework for Freedom, Truth and Justice.",
- "keywords": [
- "BDD",
- "Behavior-Driven Development",
- "Monkey Patching",
- "TDD",
- "mock",
- "stub",
- "testing",
- "unit test"
+ "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-04-25T21:27:19+00:00"
+ "time": "2020-10-26T13:12:34+00:00"
},
{
- "name": "paragonie/random_compat",
- "version": "v9.99.99",
+ "name": "sebastian/object-reflector",
+ "version": "2.0.4",
"source": {
"type": "git",
- "url": "https://github.com/paragonie/random_compat.git",
- "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
- "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"shasum": ""
},
"require": {
- "php": "^7"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "4.*|5.*",
- "vimeo/psalm": "^1"
- },
- "suggest": {
- "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Paragon Initiative Enterprises",
- "email": "security@paragonie.com",
- "homepage": "https://paragonie.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
}
],
- "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
- "keywords": [
- "csprng",
- "polyfill",
- "pseudorandom",
- "random"
+ "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": "2018-07-02T15:55:56+00:00"
+ "time": "2020-10-26T13:14:26+00:00"
},
{
- "name": "php-cs-fixer/diff",
- "version": "v1.3.0",
+ "name": "sebastian/recursion-context",
+ "version": "4.0.4",
"source": {
"type": "git",
- "url": "https://github.com/PHP-CS-Fixer/diff.git",
- "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756"
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756",
- "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^5.7.23 || ^6.4.3",
- "symfony/process": "^3.3"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
"autoload": {
"classmap": [
"src/"
@@ -979,216 +3227,237 @@
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
- },
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
- "name": "SpacePossum"
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
}
],
- "description": "sebastian/diff v2 backport support for PHP5.6",
- "homepage": "https://github.com/PHP-CS-Fixer",
- "keywords": [
- "diff"
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2018-02-15T16:58:55+00:00"
+ "time": "2020-10-26T13:17:30+00:00"
},
{
- "name": "psr/container",
- "version": "1.0.0",
+ "name": "sebastian/resource-operations",
+ "version": "3.0.3",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/container.git",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
- "psr-4": {
- "Psr\\Container\\": "src/"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
}
],
- "description": "Common Container Interface (PHP FIG PSR-11)",
- "homepage": "https://github.com/php-fig/container",
- "keywords": [
- "PSR-11",
- "container",
- "container-interface",
- "container-interop",
- "psr"
- ],
- "time": "2017-02-14T16:28:37+00:00"
+ "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": "psr/event-dispatcher",
- "version": "1.0.0",
+ "name": "sebastian/type",
+ "version": "3.2.0",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/event-dispatcher.git",
- "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
- "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
"shasum": ""
},
"require": {
- "php": ">=7.2.0"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "3.2-dev"
}
},
"autoload": {
- "psr-4": {
- "Psr\\EventDispatcher\\": "src/"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Standard interfaces for event handling.",
- "keywords": [
- "events",
- "psr",
- "psr-14"
+ "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.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2019-01-08T18:20:26+00:00"
+ "time": "2022-09-12T14:47:03+00:00"
},
{
- "name": "symfony/deprecation-contracts",
- "version": "v2.1.3",
+ "name": "sebastian/version",
+ "version": "3.0.2",
"source": {
"type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14"
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5e20b83385a77593259c9f8beb2c43cd03b2ac14",
- "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.1-dev"
- },
- "thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
- "files": [
- "function.php"
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
+ "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": [
{
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "A generic function and convention to trigger deprecation notices",
- "homepage": "https://symfony.com",
- "time": "2020-06-06T08:49:21+00:00"
+ "time": "2020-09-28T06:39:44+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v5.1.2",
+ "version": "v6.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "cc0d059e2e997e79ca34125a52f3e33de4424ac7"
+ "reference": "a0449a7ad7daa0f7c0acd508259f80544ab5a347"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/cc0d059e2e997e79ca34125a52f3e33de4424ac7",
- "reference": "cc0d059e2e997e79ca34125a52f3e33de4424ac7",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a0449a7ad7daa0f7c0acd508259f80544ab5a347",
+ "reference": "a0449a7ad7daa0f7c0acd508259f80544ab5a347",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/deprecation-contracts": "^2.1",
- "symfony/event-dispatcher-contracts": "^2",
- "symfony/polyfill-php80": "^1.15"
+ "php": ">=8.1",
+ "symfony/event-dispatcher-contracts": "^2|^3"
},
"conflict": {
- "symfony/dependency-injection": "<4.4"
+ "symfony/dependency-injection": "<5.4"
},
"provide": {
"psr/event-dispatcher-implementation": "1.0",
- "symfony/event-dispatcher-implementation": "2.0"
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "^4.4|^5.0",
- "symfony/dependency-injection": "^4.4|^5.0",
- "symfony/expression-language": "^4.4|^5.0",
- "symfony/http-foundation": "^4.4|^5.0",
- "symfony/service-contracts": "^1.1|^2",
- "symfony/stopwatch": "^4.4|^5.0"
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/stopwatch": "^5.4|^6.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
@@ -1211,26 +3480,43 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony EventDispatcher Component",
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
- "time": "2020-05-20T17:43:50+00:00"
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-05T16:51:07+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v2.1.3",
+ "version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "f6f613d74cfc5a623fc36294d3451eb7fa5a042b"
+ "reference": "02ff5eea2f453731cfbc6bc215e456b781480448"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f6f613d74cfc5a623fc36294d3451eb7fa5a042b",
- "reference": "f6f613d74cfc5a623fc36294d3451eb7fa5a042b",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/02ff5eea2f453731cfbc6bc215e456b781480448",
+ "reference": "02ff5eea2f453731cfbc6bc215e456b781480448",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.1",
"psr/event-dispatcher": "^1"
},
"suggest": {
@@ -1239,7 +3525,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.1-dev"
+ "dev-main": "3.1-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1275,32 +3561,45 @@
"interoperability",
"standards"
],
- "time": "2020-07-06T13:23:11+00:00"
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-25T11:15:52+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v5.1.2",
+ "version": "v6.1.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "6e4320f06d5f2cce0d96530162491f4465179157"
+ "reference": "4d216a2beef096edf040a070117c39ca2abce307"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/6e4320f06d5f2cce0d96530162491f4465179157",
- "reference": "6e4320f06d5f2cce0d96530162491f4465179157",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d216a2beef096edf040a070117c39ca2abce307",
+ "reference": "4d216a2beef096edf040a070117c39ca2abce307",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-ctype": "~1.8"
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
@@ -1323,33 +3622,48 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Filesystem Component",
+ "description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
- "time": "2020-05-30T20:35:19+00:00"
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v6.1.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-09-21T20:29:40+00:00"
},
{
"name": "symfony/finder",
- "version": "v5.1.2",
+ "version": "v6.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187"
+ "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/4298870062bfc667cb78d2b379be4bf5dec5f187",
- "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709",
+ "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709",
"shasum": ""
},
"require": {
- "php": ">=7.2.5"
+ "php": ">=8.1"
},
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
+ "require-dev": {
+ "symfony/filesystem": "^6.0"
},
+ "type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
@@ -1372,35 +3686,46 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Finder Component",
+ "description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
- "time": "2020-05-20T17:43:50+00:00"
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v6.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-07-29T07:42:06+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v5.1.2",
+ "version": "v6.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "663f5dd5e14057d1954fe721f9709d35837f2447"
+ "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/663f5dd5e14057d1954fe721f9709d35837f2447",
- "reference": "663f5dd5e14057d1954fe721f9709d35837f2447",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4",
+ "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/deprecation-contracts": "^2.1",
- "symfony/polyfill-php80": "^1.15"
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
@@ -1423,37 +3748,53 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony OptionsResolver Component",
+ "description": "Provides an improved replacement for the array_replace PHP function",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
],
- "time": "2020-05-23T13:08:13+00:00"
+ "support": {
+ "source": "https://github.com/symfony/options-resolver/tree/v6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-25T11:15:52+00:00"
},
{
- "name": "symfony/polyfill-php70",
- "version": "v1.18.0",
+ "name": "symfony/polyfill-php81",
+ "version": "v1.26.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php70.git",
- "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3"
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
- "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
+ "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
"shasum": ""
},
"require": {
- "paragonie/random_compat": "~1.0|~2.0|~9.99",
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1461,12 +3802,12 @@
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php70\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php81\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -1485,7 +3826,7 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+ "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
@@ -1493,91 +3834,43 @@
"portable",
"shim"
],
- "time": "2020-07-14T12:35:20+00:00"
- },
- {
- "name": "symfony/polyfill-php72",
- "version": "v1.18.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "639447d008615574653fb3bc60d1986d7172eaae"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
- "reference": "639447d008615574653fb3bc60d1986d7172eaae",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.18-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
},
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php72\\": ""
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "url": "https://github.com/fabpot",
+ "type": "github"
},
{
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
}
],
- "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2022-05-24T11:49:31+00:00"
},
{
"name": "symfony/process",
- "version": "v5.1.2",
+ "version": "v6.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1"
+ "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1",
- "reference": "7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1",
+ "url": "https://api.github.com/repos/symfony/process/zipball/a6506e99cfad7059b1ab5cab395854a0a0c21292",
+ "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-php80": "^1.15"
+ "php": ">=8.1"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
@@ -1600,45 +3893,53 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Process Component",
+ "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
- "time": "2020-05-30T20:35:19+00:00"
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v6.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-06-27T17:24:16+00:00"
},
{
- "name": "symfony/service-contracts",
- "version": "v2.1.3",
+ "name": "symfony/stopwatch",
+ "version": "v6.1.5",
"source": {
"type": "git",
- "url": "https://github.com/symfony/service-contracts.git",
- "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442"
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442",
- "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/266636bb8f3fbdccc302491df7b3a1b9a8c238a7",
+ "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "psr/container": "^1.0"
- },
- "suggest": {
- "symfony/service-implementation": ""
+ "php": ">=8.1",
+ "symfony/service-contracts": "^1|^2|^3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.1-dev"
- },
- "thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
- }
- },
"autoload": {
"psr-4": {
- "Symfony\\Contracts\\Service\\": ""
- }
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1646,75 +3947,84 @@
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Generic abstractions related to writing services",
+ "description": "Provides a way to profile code",
"homepage": "https://symfony.com",
- "keywords": [
- "abstractions",
- "contracts",
- "decoupling",
- "interfaces",
- "interoperability",
- "standards"
+ "support": {
+ "source": "https://github.com/symfony/stopwatch/tree/v6.1.5"
+ },
+ "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": "2020-07-06T13:23:11+00:00"
+ "time": "2022-09-28T16:00:52+00:00"
},
{
- "name": "symfony/stopwatch",
- "version": "v5.1.2",
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/stopwatch.git",
- "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323"
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/0f7c58cf81dbb5dd67d423a89d577524a2ec0323",
- "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/service-contracts": "^1.0|^2"
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Symfony\\Component\\Stopwatch\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ },
+ "funding": [
{
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "url": "https://github.com/theseer",
+ "type": "github"
}
],
- "description": "Symfony Stopwatch Component",
- "homepage": "https://symfony.com",
- "time": "2020-05-20T17:43:50+00:00"
+ "time": "2021-07-28T10:34:58+00:00"
}
],
"aliases": [],
@@ -1723,7 +4033,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": "^7.2"
+ "php": "^8.1"
},
- "platform-dev": []
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..e723099
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ src
+
+
+
+
+
+
+ tests
+
+
diff --git a/spec/fetch-networks.php b/spec/fetch-networks.php
deleted file mode 100644
index 485135e..0000000
--- a/spec/fetch-networks.php
+++ /dev/null
@@ -1,23 +0,0 @@
- ['image' => 'bar']];
-
- expect(fetchNetworks($configuration))->toBe([]);
- });
- });
-
- describe('from a version 2 configuration', function () {
- it('should fetch networks from the dedicated section', function () {
- $configuration = ['version' => 2, 'networks' => ['foo' => [], 'bar' => []]];
-
- expect(fetchNetworks($configuration))->toBe($configuration['networks']);
- });
- });
-});
diff --git a/spec/fetch-services.php b/spec/fetch-services.php
deleted file mode 100644
index d7ac077..0000000
--- a/spec/fetch-services.php
+++ /dev/null
@@ -1,23 +0,0 @@
- ['image' => 'bar'], 'baz' => ['build' => '.']];
-
- expect(fetchServices($configuration))->toBe($configuration);
- });
- });
-
- describe('from a version 2 configuration', function () {
- it('should fetch services from the dedicated section', function () {
- $configuration = ['version' => 2, 'services' => ['foo' => ['image' => 'bar'], 'baz' => ['build' => '.']]];
-
- expect(fetchServices($configuration))->toBe($configuration['services']);
- });
- });
-});
diff --git a/spec/fetch-volumes.php b/spec/fetch-volumes.php
deleted file mode 100644
index ff1fc16..0000000
--- a/spec/fetch-volumes.php
+++ /dev/null
@@ -1,23 +0,0 @@
- ['image' => 'bar']];
-
- expect(fetchVolumes($configuration))->toBe([]);
- });
- });
-
- describe('from a version 2 configuration', function () {
- it('should fetch volumes from the dedicated section', function () {
- $configuration = ['version' => 2, 'volumes' => ['foo' => [], 'bar' => []]];
-
- expect(fetchVolumes($configuration))->toBe($configuration['volumes']);
- });
- });
-});
diff --git a/spec/fixtures/read-configuration/invalid.yml b/spec/fixtures/read-configuration/invalid.yml
deleted file mode 100644
index 2873f33..0000000
--- a/spec/fixtures/read-configuration/invalid.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-version
-services:
- foo:
- image: bar
diff --git a/spec/fixtures/read-configuration/valid.yml b/spec/fixtures/read-configuration/valid.yml
deleted file mode 100644
index d7341a6..0000000
--- a/spec/fixtures/read-configuration/valid.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-version: 2
-services:
- foo:
- image: bar
diff --git a/spec/read-configuratoin.php b/spec/read-configuratoin.php
deleted file mode 100644
index 2262345..0000000
--- a/spec/read-configuratoin.php
+++ /dev/null
@@ -1,26 +0,0 @@
-toThrow(new InvalidArgumentException());
- });
-
- it('should parse YAML and return an array', function () {
- expect(readConfiguration(__DIR__.'/fixtures/read-configuration/valid.yml'))
- ->toBe(['version' => 2, 'services' => ['foo' => ['image' => 'bar']]]);
- });
-
- it('should report if YAML is invalid', function () {
- expect(function () {
- readConfiguration(__DIR__.'/fixtures/read-configuration/invalid.yml');
- })
- ->toThrow(new InvalidArgumentException());
- });
-});
diff --git a/src/application.php b/src/application.php
index 75c1016..32aecf8 100644
--- a/src/application.php
+++ b/src/application.php
@@ -1,5 +1,7 @@
register('render')
- ->addArgument('input-file', Console\Input\InputArgument::OPTIONAL, 'Path to a docker compose file', getcwd().DIRECTORY_SEPARATOR.'docker-compose.yml')
-
- ->addOption('override', null, Console\Input\InputOption::VALUE_REQUIRED, 'Tag of the override file to use', 'override')
+ ->addArgument('input-file', Console\Input\InputArgument::OPTIONAL | Console\Input\InputArgument::IS_ARRAY, 'Path to a docker compose file', [getcwd() . DIRECTORY_SEPARATOR . 'docker-compose.yml'])
->addOption('output-file', 'o', Console\Input\InputOption::VALUE_REQUIRED, 'Path to a output file (Only for "dot" and "image" output format)')
->addOption('output-format', 'm', Console\Input\InputOption::VALUE_REQUIRED, 'Output format (one of: "dot", "image", "display", "graphviz")', 'display')
->addOption('graphviz-output-format', null, Console\Input\InputOption::VALUE_REQUIRED, 'GraphViz Output format (see `man dot` for details)', 'svg')
- ->addOption('only', null, Console\Input\InputOption::VALUE_IS_ARRAY | Console\Input\InputOption::VALUE_REQUIRED, 'Display a graph only for a given services')
-
+ ->addOption('include', null, Console\Input\InputOption::VALUE_IS_ARRAY | Console\Input\InputOption::VALUE_REQUIRED, 'Display a graph only for given services')
+ ->addOption('exclude', null, Console\Input\InputOption::VALUE_IS_ARRAY | Console\Input\InputOption::VALUE_REQUIRED, 'Display a graph without the given services')
->addOption('force', 'f', Console\Input\InputOption::VALUE_NONE, 'Overwrites output file if it already exists')
->addOption('no-volumes', null, Console\Input\InputOption::VALUE_NONE, 'Do not display volumes')
->addOption('no-networks', null, Console\Input\InputOption::VALUE_NONE, 'Do not display networks')
->addOption('no-ports', null, Console\Input\InputOption::VALUE_NONE, 'Do not display ports')
+ ->addOption('no-secrets', null, Console\Input\InputOption::VALUE_NONE, 'Do not display secrets')
+ ->addOption('no-configs', null, Console\Input\InputOption::VALUE_NONE, 'Do not display configs')
->addOption('horizontal', 'r', Console\Input\InputOption::VALUE_NONE, 'Display a horizontal graph')
->addOption('ignore-override', null, Console\Input\InputOption::VALUE_NONE, 'Ignore override file')
->addOption('background', null, Console\Input\InputOption::VALUE_REQUIRED, 'Set the graph background color', '#ffffff')
-
->setCode(function (Console\Input\InputInterface $input, Console\Output\OutputInterface $output) {
$backgroundColor = $input->getOption('background');
@@ -32,15 +33,15 @@
}
$logger = logger($output);
- $inputFile = $input->getArgument('input-file');
- $inputFileExtension = pathinfo($inputFile, PATHINFO_EXTENSION);
- $overrideFile = dirname($inputFile).DIRECTORY_SEPARATOR.basename($inputFile, '.'.$inputFileExtension).'.'.$input->getOption('override').'.'.$inputFileExtension;
+ $inputFiles = $input->getArgument('input-file');
+
$outputFormat = $input->getOption('output-format');
- $outputFile = $input->getOption('output-file') ?: getcwd().DIRECTORY_SEPARATOR.'docker-compose.'.('dot' === $outputFormat ? $outputFormat : 'png');
- $onlyServices = $input->getOption('only');
+ $outputFile = $input->getOption('output-file') ?: getcwd() . DIRECTORY_SEPARATOR . 'docker-compose.' . ('dot' === $outputFormat ? $outputFormat : 'png');
+ $includeServices = $input->getOption('include');
+ $excludeServices = $input->getOption('exclude');
- if (false === in_array($outputFormat, ['dot', 'image', 'display', 'graphviz'])) {
+ if (false === in_array($outputFormat, ['dot', 'image', 'display', 'graphviz'], true)) {
throw new Console\Exception\InvalidArgumentException(sprintf('Invalid output format "%s". It must be one of "dot", "image" or "display".', $outputFormat));
}
@@ -54,24 +55,9 @@
}
}
- $logger(sprintf('Reading configuration from "%s"', $inputFile));
- $configuration = readConfiguration($inputFile);
- $configurationVersion = (string) ($configuration['version'] ?? 1);
-
- if (!$input->getOption('ignore-override') && file_exists($overrideFile)) {
- $logger(sprintf('Reading override from "%s"', $overrideFile));
- $override = readConfiguration($overrideFile);
- $overrideVersion = (string) ($override['version'] ?? 1);
-
- if ($configurationVersion !== $overrideVersion) {
- throw new Console\Exception\LogicException(sprintf('Version mismatch: file "%s" specifies version "%s" but file "%s" uses version "%s"', $inputFile, $configurationVersion, $overrideFile, $overrideVersion));
- }
-
- $configuration = array_merge_recursive($configuration, $override);
-
- $logger(sprintf('Configuration version is "%s"', $configurationVersion), Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE);
- $configuration['version'] = $configurationVersion;
- }
+ $files = findConfigurationFiles($input->getOption('ignore-override'), ...$inputFiles);
+ $logger(sprintf('Reading configuration from "%s"', implode(', ', $files)));
+ $configuration = readConfigurations(...$files);
$logger('Fetching services');
$services = fetchServices($configuration);
@@ -85,22 +71,20 @@
$networks = fetchNetworks($configuration);
$logger(sprintf('Found %d networks', count($networks)), Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE);
- if ([] !== $onlyServices) {
- $logger(sprintf('Only %s services will be displayed', implode(', ', $onlyServices)));
+ $logger('Fetching configs');
+ $configs = fetchConfigs($configuration);
+ $logger(sprintf('Found %d configs', count($configs)), Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE);
- $intersect = array_intersect($onlyServices, array_keys($services));
+ $logger('Fetching secrets');
+ $secrets = fetchSecrets($configuration);
+ $logger(sprintf('Found %d secrets', count($secrets)), Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE);
- if ($intersect !== $onlyServices) {
- throw new Console\Exception\InvalidArgumentException(sprintf('The following services do not exist: "%s"', implode('", "', array_diff($onlyServices, $intersect))));
- }
+ if ([] !== $includeServices) {
+ $logger(sprintf('Only %s services will be displayed', implode(', ', $includeServices)));
+ }
- $services = array_filter(
- $services,
- function ($service) use ($onlyServices) {
- return in_array($service, $onlyServices);
- },
- ARRAY_FILTER_USE_KEY
- );
+ if ([] !== $excludeServices) {
+ $logger(sprintf('Services %s will not be displayed', implode(', ', $excludeServices)));
}
$flags = 0;
@@ -122,17 +106,53 @@ function ($service) use ($onlyServices) {
$flags |= WITHOUT_PORTS;
}
+ if (true === $input->getOption('no-secrets')) {
+ $logger('Secrets will not be displayed');
+
+ $flags |= WITHOUT_SECRETS;
+ }
+
+ if (true === $input->getOption('no-configs')) {
+ $logger('Configs will not be displayed');
+
+ $flags |= WITHOUT_CONFIGS;
+ }
+
$logger('Rendering graph');
$graph = applyGraphvizStyle(
- createGraph($services, $volumes, $networks, $inputFile, $flags),
+ createGraph($services, $volumes, $networks, $configs, $secrets, $flags, $inputFiles[0]),
$input->getOption('horizontal'),
$input->getOption('background')
);
+ if ([] !== $includeServices) {
+ foreach ($graph->getVertices() as $vertex) {
+ if ($vertex->getAttribute('docker_compose_viz.type') !== 'service') {
+ continue;
+ }
+
+ if (!in_array($vertex->getId(), $includeServices, true)) {
+ $vertex->destroy();
+ }
+ }
+ }
+
+ if ([] !== $excludeServices) {
+ foreach ($graph->getVertices() as $vertex) {
+ if ($vertex->getAttribute('docker_compose_viz.type') !== 'service') {
+ continue;
+ }
+
+ if (in_array($vertex->getId(), $includeServices, true)) {
+ $vertex->destroy();
+ }
+ }
+ }
+
switch ($outputFormat) {
case 'dot':
case 'image':
- $rendererClass = 'Graphp\GraphViz\\'.ucfirst($outputFormat);
+ $rendererClass = 'Graphp\GraphViz\\' . ucfirst($outputFormat);
$renderer = new $rendererClass();
file_put_contents($outputFile, $renderer->getOutput($graph));
@@ -152,4 +172,4 @@ function ($service) use ($onlyServices) {
}
});
-$application->run();
+return $application;
diff --git a/src/config.php b/src/config.php
new file mode 100644
index 0000000..e8e4790
--- /dev/null
+++ b/src/config.php
@@ -0,0 +1,75 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'config');
+ $vertex->setAttribute('graphviz.label', $label);
+ $vertex->setAttribute('graphviz.shape', 'note');
+
+ return $vertex;
+}
+
+function findConfigVertex(Graph $graph, string $config): Vertex
+{
+ return $graph->getVertex(getConfigVertexId($config));
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#configs
+ */
+function addConfigRelation(Graph $graph, string $service, string $config, ?array $mapping = null): void
+{
+ $serviceVertex = $graph->getVertex(getServiceVertexId($service));
+ $configVertex = findConfigVertex($graph, $config);
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($configVertex)) {
+ $edges = $serviceVertex->getEdgesTo($configVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'config') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($configVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'config');
+
+ if (isset($mapping['target'])) {
+ $edge->setAttribute('graphviz.label', $mapping['target']);
+ }
+}
diff --git a/src/functions.php b/src/functions.php
index 72a7128..2f53a2a 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -1,11 +1,12 @@
getCode(), $exception);
+ if (file_exists($overrideFile)) {
+ return $overrideFile;
}
+
+ return null;
}
-/**
- * @public
- *
- * @param array $configuration Docker compose (version 1 or 2) configuration
- *
- * @return array List of service definitions exctracted from the configuration
- */
-function fetchServices(array $configuration): array
+function findConfigurationFiles(bool $ignoreOverride = false, string ...$paths): array
{
- if (false === isset($configuration['version']) || 1 === (int) $configuration['version']) {
- return $configuration;
- }
+ $files = [];
- return $configuration['services'] ?? [];
-}
+ foreach ($paths as $path) {
+ if (false === file_exists($path)) {
+ throw new InvalidArgumentException(sprintf('File "%s" does not exist', $path));
+ }
-/**
- * @public
- *
- * @param array $configuration Docker compose (version 1 or 2) configuration
- *
- * @return array List of service definitions exctracted from the configuration
- */
-function fetchVolumes(array $configuration): array
-{
- if (false === isset($configuration['version']) || 1 === (int) $configuration['version']) {
- return [];
+ $files[] = $path;
+
+ if (false === $ignoreOverride) {
+ $override = findOverride($path);
+
+ if (null !== $override) {
+ $files[] = $override;
+ }
+ }
}
- return $configuration['volumes'] ?? [];
+ return $files;
}
-/**
- * @public
- *
- * @param array $configuration Docker compose (version 1 or 2) configuration
- *
- * @return array List of service definitions exctracted from the configuration
- */
-function fetchNetworks(array $configuration): array
+function readConfigurations(string ...$paths): array
{
- if (false === isset($configuration['version']) || 1 === (int) $configuration['version']) {
- return [];
+ $configuration = [];
+
+ foreach ($paths as $path) {
+ if (false === file_exists($path)) {
+ throw new InvalidArgumentException(sprintf('File "%s" does not exist', $path));
+ }
+
+ try {
+ $configuration = array_merge_recursive($configuration, Yaml::parse(file_get_contents($path)));
+ } catch (ParseException $exception) {
+ throw new InvalidArgumentException(sprintf('File "%s" does not contain valid YAML', $path), $exception->getCode(), $exception);
+ }
}
- return $configuration['networks'] ?? [];
+ return $configuration;
}
-/**
- * @public
- *
- * @param array $services Docker compose service definitions
- * @param array $volumes Docker compose volume definitions
- * @param array $networks Docker compose network definitions
- * @param bool $withVolumes Create vertices and edges for volumes
- * @param string $path Path of the current docker-compose configuration file
- *
- * @return Graph The complete graph for the given list of services
- */
-function createGraph(array $services, array $volumes, array $networks, string $path, int $flags): Graph
+function createGraph(array $services, array $volumes, array $networks, array $configs, array $secrets, int $flags, string $path): Graph
{
- return makeVerticesAndEdges(new Graph(), $services, $volumes, $networks, $path, $flags);
+ return makeVerticesAndEdges(new Graph(), $services, $volumes, $networks, $configs, $secrets, $flags, $path);
}
-/**
- * @public
- *
- * @param Graph $graph Input graph
- * @param bool $horizontal Display a horizontal graph
- * @param string $horizontal Background color (any hex color or 'transparent')
- *
- * @return Graph A copy of the input graph with style attributes
- */
-function applyGraphvizStyle(Graph $graph, bool $horizontal, string $background): Graph
+function makeVerticesAndEdges(Graph $graph, array $services, array $volumes, array $networks, array $configs, array $secrets, int $flags, string $path): Graph
{
- $graph = $graph->createGraphClone();
- $graph->setAttribute('graphviz.graph.bgcolor', $background);
- $graph->setAttribute('graphviz.graph.pad', '0.5');
- $graph->setAttribute('graphviz.graph.ratio', 'fill');
-
- if (true === $horizontal) {
- $graph->setAttribute('graphviz.graph.rankdir', 'LR');
+ if (false === ((bool)($flags & WITHOUT_VOLUMES))) {
+ iterator_apply($it = new \ArrayIterator($volumes), fn (Iterator $it) => addVolume(
+ $graph,
+ $it->key(),
+ VolumeKind::VOLUME
+ ), [$it]);
}
- foreach ($graph->getVertices() as $vertex) {
- switch ($vertex->getAttribute('docker_compose.type')) {
- case 'service':
- $vertex->setAttribute('graphviz.shape', 'component');
- break;
-
- case 'external_service':
- $vertex->setAttribute('graphviz.shape', 'component');
- $vertex->setAttribute('graphviz.color', 'gray');
- break;
-
- case 'volume':
- $vertex->setAttribute('graphviz.shape', 'folder');
- break;
-
- case 'network':
- $vertex->setAttribute('graphviz.shape', 'pentagon');
- break;
-
- case 'external_network':
- $vertex->setAttribute('graphviz.shape', 'pentagon');
- $vertex->setAttribute('graphviz.color', 'gray');
- break;
-
- case 'port':
- $vertex->setAttribute('graphviz.shape', 'circle');
-
- if ('udp' === ($proto = $vertex->getAttribute('docker_compose.proto'))) {
- $vertex->setAttribute('graphviz.style', 'dashed');
- }
- break;
- }
+ if (false === ((bool)($flags & WITHOUT_NETWORKS))) {
+ iterator_apply($it = new \ArrayIterator($networks), fn (Iterator $it) => addNetwork(
+ $graph,
+ $it->key(),
+ $it->current()
+ ), [$it]);
}
- foreach ($graph->getEdges() as $edge) {
- switch ($edge->getAttribute('docker_compose.type')) {
- case 'ports':
- case 'links':
- $edge->setAttribute('graphviz.style', 'solid');
- break;
-
- case 'external_links':
- $edge->setAttribute('graphviz.style', 'solid');
- $edge->setAttribute('graphviz.color', 'gray');
- break;
-
- case 'volumes_from':
- case 'volumes':
- $edge->setAttribute('graphviz.style', 'dashed');
- break;
-
- case 'depends_on':
- $edge->setAttribute('graphviz.style', 'dotted');
- break;
-
- case 'extends':
- $edge->setAttribute('graphviz.dir', 'both');
- $edge->setAttribute('graphviz.arrowhead', 'inv');
- $edge->setAttribute('graphviz.arrowtail', 'dot');
- break;
- }
-
- if (null !== ($alias = $edge->getAttribute('docker_compose.alias'))) {
- $edge->setAttribute('graphviz.label', $alias);
-
- if (null !== $edge->getAttribute('docker_compose.condition')) {
- $edge->setAttribute('graphviz.fontsize', '10');
- }
- }
-
- if ($edge->getAttribute('docker_compose.bidir')) {
- $edge->setAttribute('graphviz.dir', 'both');
- }
+ if (false === ((bool)($flags & WITHOUT_CONFIGS))) {
+ iterator_apply($it = new \ArrayIterator($configs), fn (Iterator $it) => addConfig($graph, $it->key()), [$it]);
}
- return $graph;
-}
-
-/**
- * @internal
- *
- * @param Graph $graph Input graph
- * @param array $services Docker compose service definitions
- * @param array $volumes Docker compose volume definitions
- * @param array $networks Docker compose network definitions
- * @param bool $withVolumes Create vertices and edges for volumes
- *
- * @return Graph A copy of the input graph with vertices and edges for services
- */
-function makeVerticesAndEdges(Graph $graph, array $services, array $volumes, array $networks, string $path, int $flags): Graph
-{
- if (false === ((bool) ($flags & WITHOUT_VOLUMES))) {
- foreach (array_keys($volumes) as $volume) {
- addVolume($graph, 'named: '.$volume);
- }
+ if (false === ((bool)($flags & WITHOUT_SECRETS))) {
+ iterator_apply($it = new \ArrayIterator($secrets), fn (Iterator $it) => addSecret($graph, $it->key()), [$it]);
}
- if (false === ((bool) ($flags & WITHOUT_NETWORKS))) {
- foreach ($networks as $network => $definition) {
- addNetwork(
- $graph,
- 'net: '.$network,
- isset($definition['external']) && true === $definition['external'] ? 'external_network' : 'network'
- );
- }
- }
+ iterator_apply($it = new \ArrayIterator($services), fn (Iterator $it) => addService($graph, $it->key()), [$it]);
foreach ($services as $service => $definition) {
- addService($graph, $service);
-
if (isset($definition['extends'])) {
if (isset($definition['extends']['file'])) {
- $configuration = readConfiguration(dirname($path).DIRECTORY_SEPARATOR.$definition['extends']['file']);
+ $extendedFile = dirname($path) . DIRECTORY_SEPARATOR . $definition['extends']['file'];
+ $configuration = readConfigurations($extendedFile);
$extendedServices = fetchServices($configuration);
$extendedVolumes = fetchVolumes($configuration);
$extendedNetworks = fetchNetworks($configuration);
-
- $graph = makeVerticesAndEdges($graph, $extendedServices, $extendedVolumes, $extendedNetworks, dirname($path).DIRECTORY_SEPARATOR.$definition['extends']['file'], $flags);
+ $extendedConfigs = fetchNetworks($configuration);
+ $extendedSecrets = fetchNetworks($configuration);
+
+ $graph = makeVerticesAndEdges(
+ $graph,
+ $extendedServices,
+ $extendedVolumes,
+ $extendedNetworks,
+ $extendedConfigs,
+ $extendedSecrets,
+ $flags,
+ $path
+ );
}
- addRelation(
- addService($graph, $definition['extends']['service']),
- $graph->getVertex($service),
- 'extends'
- );
- }
-
- $serviceLinks = [];
-
- foreach ($definition['links'] ?? [] as $link) {
- list($target, $alias) = explodeMapping($link);
-
- $serviceLinks[$alias] = $target;
+ addExtendsRelation($graph, $service, $definition['extends']['service']);
}
- foreach ($serviceLinks as $alias => $target) {
- addRelation(
- addService($graph, $target),
- $graph->getVertex($service),
- 'links',
- $alias !== $target ? $alias : null
- );
- }
+ iterator_apply($it = new \ArrayIterator($definition['links'] ?? []), fn (Iterator $it) => addLinkRelation(
+ $graph,
+ $service,
+ ...normalizeLinkMapping($it->current())
+ ), [$it]);
- foreach ($definition['external_links'] ?? [] as $link) {
- list($target, $alias) = explodeMapping($link);
+ iterator_apply($it = new \ArrayIterator($definition['external_links'] ?? []), fn (Iterator $it) => addExternalLinkRelation(
+ $graph,
+ $service,
+ ...normalizeLinkMapping($it->current())
+ ), [$it]);
- addRelation(
- addService($graph, $target, 'external_service'),
- $graph->getVertex($service),
- 'external_links',
- $alias !== $target ? $alias : null
- );
- }
+ iterator_apply($it = new \ArrayIterator($definition['depends_on'] ?? []), fn (Iterator $it) => addDependsRelation(
+ $graph,
+ $service,
+ is_array($it->current()) ? $it->key() : $it->current(),
+ $it->current()['condition'] ?? null
+ ), [$it]);
- foreach ($definition['depends_on'] ?? [] as $key => $dependency) {
- addRelation(
- $graph->getVertex($service),
- addService($graph, is_array($dependency) ? $key : $dependency),
- 'depends_on',
- is_array($dependency) && isset($dependency['condition']) ? $dependency['condition'] : null,
- false,
- is_array($dependency) && isset($dependency['condition'])
- );
- }
-
- foreach ($definition['volumes_from'] ?? [] as $source) {
- addRelation(
- addService($graph, $source),
- $graph->getVertex($service),
- 'volumes_from'
- );
- }
-
- if (false === ((bool) ($flags & WITHOUT_VOLUMES))) {
- $serviceVolumes = [];
+ if (false === ((bool)($flags & WITHOUT_VOLUMES))) {
+ iterator_apply(new \ArrayIterator($definition['volumes_from'] ?? []), fn (Iterator $it) => addVolumesFromRelation(
+ $graph,
+ $it->current(),
+ $service
+ ), [$it]);
foreach ($definition['volumes'] ?? [] as $volume) {
- if (is_array($volume)) {
- $host = $volume['source'];
- $container = $volume['target'];
- $attr = !empty($volume['read-only']) ? 'ro' : '';
- } else {
- list($host, $container, $attr) = explodeVolumeMapping($volume);
- }
-
- $serviceVolumes[$container] = [$host, $attr];
- }
-
- foreach ($serviceVolumes as $container => $volume) {
- list($host, $attr) = $volume;
-
- if ('.' !== $host[0] && DIRECTORY_SEPARATOR !== $host[0]) {
- $host = 'named: '.$host;
- }
-
- addRelation(
- addVolume($graph, $host),
- $graph->getVertex($service),
- 'volumes',
- $host !== $container ? $container : null,
- 'ro' !== $attr
- );
+ $volume = normalizeVolumeMapping($volume, $volumes);
+ addVolume($graph, $volume['source'], VolumeKind::BIND);
+ addVolumeRelation($graph, $service, $volume);
}
}
- if (false === ((bool) ($flags & WITHOUT_PORTS))) {
- foreach ($definition['ports'] ?? [] as $port) {
- list($target, $host, $container, $proto) = explodePortMapping($port);
-
- addRelation(
- addPort($graph, (int) $host, $proto, $target),
- $graph->getVertex($service),
- 'ports',
- $host !== $container ? $container : null
- );
- }
+ if (false === ((bool)($flags & WITHOUT_PORTS))) {
+ iterator_apply($it = new \ArrayIterator($definition['ports'] ?? []), fn (Iterator $it) => addPortRelation(
+ $graph,
+ $service,
+ normalizePortMapping($it->current())
+ ), [$it]);
}
- if (false === ((bool) ($flags & WITHOUT_NETWORKS))) {
- foreach ($definition['networks'] ?? [] as $network => $config) {
- $network = is_int($network) ? $config : $network;
- $config = is_int($network) ? [] : $config;
- $aliases = $config['aliases'] ?? [];
-
- addRelation(
- $graph->getVertex($service),
- addNetwork($graph, 'net: '.$network),
- 'networks',
- count($aliases) > 0 ? implode(', ', $aliases) : null
- );
- }
+ if (false === ((bool)($flags & WITHOUT_NETWORKS))) {
+ iterator_apply($it = new \ArrayIterator($definition['networks'] ?? []), fn (Iterator $it) => addNetworkRelation(
+ $graph,
+ $service,
+ is_int($it->key()) ? $it->current() : $it->key(),
+ is_int($it->key()) ? [] : $it->current()
+ ), [$it]);
}
- }
-
- return $graph;
-}
-
-/**
- * @internal
- *
- * @param Graph $graph Input graph
- * @param string $service Service name
- * @param string $type Service type
- *
- * @return Vertex
- */
-function addService(Graph $graph, string $service, string $type = null)
-{
- if (true === $graph->hasVertex($service)) {
- return $graph->getVertex($service);
- }
-
- $vertex = $graph->createVertex($service);
- $vertex->setAttribute('docker_compose.type', $type ?: 'service');
-
- return $vertex;
-}
-
-/**
- * @internal
- *
- * @param Graph $graph Input graph
- * @param int $port Port number
- * @param string|null $proto Protocol
- *
- * @return Vertex
- */
-function addPort(Graph $graph, int $port, string $proto = null, string $target = null)
-{
- $target = $target ? $target.':' : null;
-
- if (true === $graph->hasVertex($target.$port)) {
- return $graph->getVertex($target.$port);
- }
-
- $vertex = $graph->createVertex($target.$port);
- $vertex->setAttribute('docker_compose.type', 'port');
- $vertex->setAttribute('docker_compose.proto', $proto ?: 'tcp');
-
- return $vertex;
-}
-
-/**
- * @internal
- *
- * @param Graph $graph Input graph
- * @param string $path Path
- *
- * @return Vertex
- */
-function addVolume(Graph $graph, string $path)
-{
- if (true === $graph->hasVertex($path)) {
- return $graph->getVertex($path);
- }
-
- $vertex = $graph->createVertex($path);
- $vertex->setAttribute('docker_compose.type', 'volume');
-
- return $vertex;
-}
-
-/**
- * @internal
- *
- * @param Graph $graph Input graph
- * @param string $name Name of the network
- * @param string $type Network type
- *
- * @return Vertex
- */
-function addNetwork(Graph $graph, string $name, string $type = null)
-{
- if (true === $graph->hasVertex($name)) {
- return $graph->getVertex($name);
- }
-
- $vertex = $graph->createVertex($name);
- $vertex->setAttribute('docker_compose.type', $type ?: 'network');
-
- return $vertex;
-}
-/**
- * @internal
- *
- * @param Vertex $from Source vertex
- * @param Vertex $to Destination vertex
- * @param string $type Type of the relation (one of "links", "volumes_from", "depends_on", "ports");
- * @param string|null $alias Alias associated to the linked element
- * @param bool|null $bidirectional Biderectional or not
- * @param bool|null $condition Wether the alias represents a condition or not
- */
-function addRelation(Vertex $from, Vertex $to, string $type, string $alias = null, bool $bidirectional = false, bool $condition = false): Edge\Directed
-{
- $edge = null;
-
- if ($from->hasEdgeTo($to)) {
- $edges = $from->getEdgesTo($to);
-
- foreach ($edges as $edge) {
- if ($edge->getAttribute('docker_compose.type') === $type) {
- break;
- }
+ if (false === ((bool)($flags & WITHOUT_CONFIGS))) {
+ iterator_apply($it = new \ArrayIterator($definition['configs'] ?? []), fn (Iterator $it) => addConfigRelation(
+ $graph,
+ $service,
+ is_array($it->current()) ? $it->current()['source'] : $it->current(),
+ is_array($it->current()) ? $it->current() : []
+ ), [$it]);
}
- }
-
- if (null === $edge) {
- $edge = $from->createEdgeTo($to);
- }
- $edge->setAttribute('docker_compose.type', $type);
-
- if (null !== $alias) {
- $edge->setAttribute('docker_compose.alias', $alias);
- }
-
- if (true === $condition) {
- $edge->setAttribute('docker_compose.condition', true);
+ if (false === ((bool)($flags & WITHOUT_SECRETS))) {
+ iterator_apply($it = new \ArrayIterator($definition['secrets'] ?? []), fn (Iterator $it) => addSecretRelation(
+ $graph,
+ $service,
+ $it->current()
+ ), [$it]);
+ }
}
- $edge->setAttribute('docker_compose.bidir', $bidirectional);
-
- return $edge;
-}
-
-/**
- * @internal
- *
- * @param string $mapping A docker mapping ([:])
- *
- * @return array An 2 or 3 items array containing the parts of the mapping.
- * If the mapping does not specify a second part, the first one will be repeated
- */
-function explodeMapping($mapping): array
-{
- $parts = explode(':', $mapping);
- $parts[1] = $parts[1] ?? $parts[0];
-
- return [$parts[0], $parts[1]];
+ return $graph;
}
-/**
- * @internal
- *
- * @param string $mapping A docker mapping ([:])
- *
- * @return array An 2 or 3 items array containing the parts of the mapping.
- * If the mapping does not specify a second part, the first one will be repeated
- */
-function explodeVolumeMapping($mapping): array
+function applyGraphvizStyle(Graph $graph, bool $horizontal, string $background): Graph
{
- $parts = explode(':', $mapping);
- $parts[1] = $parts[1] ?? $parts[0];
-
- return [$parts[0], $parts[1], $parts[2] ?? null];
-}
+ $graph = $graph->createGraphClone();
+ $graph->setAttribute('graphviz.graph.bgcolor', $background);
+ $graph->setAttribute('graphviz.graph.pad', '0.5');
+ $graph->setAttribute('graphviz.graph.ratio', 'fill');
+ $graph->setAttribute('graphviz.graph.splines', 'true');
+ $graph->setAttribute('graphviz.graph.overlap', 'false');
-/**
- * @internal
- *
- * @param string $mapping A docker mapping ([:])
- *
- * @return array An 2 or 3 items array containing the parts of the mapping.
- * If the mapping does not specify a second part, the first one will be repeated
- */
-function explodePortMapping($mapping): array
-{
- $parts = explode(':', $mapping);
-
- if (count($parts) < 3) {
- $target = null;
- $host = $parts[0];
- $container = $parts[1] ?? $parts[0];
- } else {
- $target = $parts[0];
- $host = $parts[1];
- $container = $parts[2];
+ if (true === $horizontal) {
+ $graph->setAttribute('graphviz.graph.rankdir', 'LR');
}
- $subparts = array_values(array_filter(explode('/', $container)));
-
- return [$target, $host, $subparts[0], $subparts[1] ?? null];
+ return $graph;
}
diff --git a/src/network.php b/src/network.php
new file mode 100644
index 0000000..1db53a6
--- /dev/null
+++ b/src/network.php
@@ -0,0 +1,76 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'network');
+ $vertex->setAttribute('graphviz.label', $label);
+ $vertex->setAttribute('graphviz.shape', 'pentagon');
+
+ if (isset($definition['external'])) {
+ $vertex->setAttribute('graphviz.color', 'gray');
+ }
+
+ return $vertex;
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#networks
+ */
+function addNetworkRelation(Graph $graph, string $service, string $network, ?array $mapping = null): void
+{
+ $serviceVertex = $graph->getVertex(getServiceVertexId($service));
+ $networkVertex = $graph->getVertex(getNetworkVertexId($network));
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($networkVertex)) {
+ $edges = $serviceVertex->getEdgesTo($networkVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'network') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($networkVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'network');
+
+ $aliases = $mapping['aliases'] ?? [];
+
+ if (count($aliases) > 0) {
+ $edge->setAttribute('graphviz.label', implode(', ', $aliases));
+ }
+}
diff --git a/src/port.php b/src/port.php
new file mode 100644
index 0000000..c0db7bc
--- /dev/null
+++ b/src/port.php
@@ -0,0 +1,109 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'port');
+ $vertex->setAttribute('graphviz.label', $label);
+ $vertex->setAttribute('graphviz.shape', 'circle');
+
+ if ('udp' === $definition['proto']) {
+ $vertex->setAttribute('graphviz.style', 'dashed');
+ }
+
+ return $vertex;
+}
+
+function addPortRelation(Graph $graph, string $service, array $definition): void
+{
+ $serviceVertex = $graph->getVertex(getServiceVertexId($service));
+ $configVertex = addPort($graph, $definition);
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($configVertex)) {
+ $edges = $serviceVertex->getEdgesTo($configVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'port') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($configVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'port');
+ $edge->setAttribute('graphviz.style', 'solid');
+
+ if (isset($definition['target'])) {
+ $edge->setAttribute('graphviz.label', $definition['target']);
+ }
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#networks
+ */
+function normalizePortMapping(string|array $mapping): array
+{
+ if (is_array($mapping)) {
+ return $mapping;
+ }
+
+ $parts = explode(':', $mapping);
+ $ip = null;
+ $published = null;
+ $target = null;
+ $proto = null;
+
+ if (count($parts) === 1) {
+ $target = $parts[0];
+ $published = $target;
+ } elseif (count($parts) === 2) {
+ $published = $parts[0];
+ $target = $parts[1];
+ } elseif (count($parts) === 3) {
+ $ip = $parts[0];
+ $published = $parts[1];
+ $target = $parts[2];
+ }
+
+ $subparts = array_values(array_filter(explode('/', $target)));
+
+ if (isset($subparts[1])) {
+ $proto = $subparts[1];
+ }
+
+ return [
+ 'host_ip' => $ip,
+ 'published' => $published,
+ 'target' => $target,
+ 'proto' => $proto,
+ ];
+}
diff --git a/src/secret.php b/src/secret.php
new file mode 100644
index 0000000..532fe70
--- /dev/null
+++ b/src/secret.php
@@ -0,0 +1,74 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'secret');
+ $vertex->setAttribute('graphviz.shape', 'hexagon');
+
+ return $vertex;
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#secrets
+ */
+function addSecretRelation(Graph $graph, string $service, string|array $mapping): void
+{
+ $serviceVertex = $graph->getVertex(getServiceVertexId($service));
+
+ if (is_string($mapping)) {
+ $configVertex = $graph->getVertex(getSecretVertexId($mapping));
+ } else {
+ $configVertex = $graph->getVertex(getSecretVertexId($mapping['source']));
+ }
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($configVertex)) {
+ $edges = $serviceVertex->getEdgesTo($configVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'secret') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($configVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'secret');
+
+ if (isset($mapping['target'])) {
+ $edge->setAttribute('graphviz.label', $mapping['target']);
+ }
+}
diff --git a/src/service.php b/src/service.php
new file mode 100644
index 0000000..1083d0a
--- /dev/null
+++ b/src/service.php
@@ -0,0 +1,225 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'service');
+ $vertex->setAttribute('graphviz.shape', 'component');
+
+ if ('external_service' === $type) {
+ $vertex->setAttribute('graphviz.color', 'gray');
+ }
+
+ return $vertex;
+}
+
+function findServiceVertex(Graph $graph, string $service): Vertex
+{
+ return $graph->getVertex(getServiceVertexId($service));
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#extends
+ */
+function addExtendsRelation(Graph $graph, string $service, string $extended): void
+{
+ $serviceVertex = findServiceVertex($graph, $service);
+ $extendedVertex = findServiceVertex($graph, $extended);
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($extendedVertex)) {
+ $edges = $serviceVertex->getEdgesTo($extendedVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'extends') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($extendedVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'extends');
+ $edge->setAttribute('graphviz.dir', 'both');
+ $edge->setAttribute('graphviz.arrowhead', 'inv');
+ $edge->setAttribute('graphviz.arrowtail', 'dot');
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#links
+ */
+function addLinkRelation(Graph $graph, string $service, string $linked, ?string $alias = null): void
+{
+ $serviceVertex = findServiceVertex($graph, $service);
+ $linkedVertex = findServiceVertex($graph, $linked);
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($linkedVertex)) {
+ $edges = $serviceVertex->getEdgesTo($linkedVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'link') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($linkedVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'link');
+ $edge->setAttribute('graphviz.style', 'solid');
+
+ if (null !== $alias) {
+ $edge->setAttribute('graphviz.label', $alias);
+ }
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#external_links
+ */
+function addExternalLinkRelation(Graph $graph, string $service, string $linked, ?string $alias = null): void
+{
+ $serviceVertex = findServiceVertex($graph, $service);
+ $linkedVertex = findServiceVertex($graph, $linked);
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($linkedVertex)) {
+ $edges = $serviceVertex->getEdgesTo($linkedVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'external_link') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($linkedVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'external_link');
+ $edge->setAttribute('graphviz.style', 'solid');
+ $edge->setAttribute('graphviz.color', 'gray');
+
+ if (null !== $alias) {
+ $edge->setAttribute('graphviz.label', $alias);
+ }
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#depends_on
+ */
+function addDependsRelation(Graph $graph, string $service, string $dependency, ?string $condition = null): void
+{
+ $serviceVertex = findServiceVertex($graph, $service);
+ $dependencyVertex = findServiceVertex($graph, $dependency);
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($dependencyVertex)) {
+ $edges = $serviceVertex->getEdgesTo($dependencyVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'depends') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($dependencyVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'depends');
+ $edge->setAttribute('graphviz.style', 'dotted');
+
+ if (null !== $condition) {
+ if (null !== $edge->getAttribute('graphviz.label')) {
+ $label = $edge->getAttribute('graphviz.label');
+ $edge->setAttribute('graphviz.label', $label . ' (' . $condition . ')');
+ } else {
+ $edge->setAttribute('graphviz.label', $condition);
+ }
+ }
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#volumes_from
+ */
+function addVolumesFromRelation(Graph $graph, string $service, string $dependency): void
+{
+ $serviceVertex = findServiceVertex($graph, $service);
+ $dependencyVertex = findServiceVertex($graph, $dependency);
+
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($dependencyVertex)) {
+ $edges = $serviceVertex->getEdgesTo($dependencyVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'volumes_from') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($dependencyVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'volumes_from');
+ $edge->setAttribute('graphviz.style', 'dashed');
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#links
+ */
+function normalizeLinkMapping(string $mapping): array
+{
+ $parts = explode(':', $mapping);
+
+ return [$parts[0], $parts[1] ?? $parts[0]];
+}
diff --git a/src/volume.php b/src/volume.php
new file mode 100644
index 0000000..6c626aa
--- /dev/null
+++ b/src/volume.php
@@ -0,0 +1,105 @@
+hasVertex($id)) {
+ return $graph->getVertex($id);
+ }
+
+ $vertex = $graph->createVertex($id);
+ $vertex->setAttribute('docker_compose_viz.type', 'volume');
+ $vertex->setAttribute('graphviz.label', $label);
+ $vertex->setAttribute('graphviz.shape', 'pentagon');
+
+ if (VolumeKind::VOLUME === $type) {
+ $vertex->setAttribute('graphviz.color', 'blue');
+ }
+
+ return $vertex;
+}
+
+function addVolumeRelation(Graph $graph, string $service, array $definition): void
+{
+ $serviceVertex = $graph->getVertex(getServiceVertexId($service));
+ $volumeVertex = $graph->getVertex(getVolumeVertexId($definition['source']));
+ $edge = null;
+
+ if ($serviceVertex->hasEdgeTo($volumeVertex)) {
+ $edges = $serviceVertex->getEdgesTo($volumeVertex);
+
+ foreach ($edges as $edge) {
+ if ($edge->getAttribute('docker_compose_viz.type') === 'volume') {
+ break;
+ }
+
+ $edge = null;
+ }
+ }
+
+ if (null === $edge) {
+ $edge = $serviceVertex->createEdgeTo($volumeVertex);
+ }
+
+ $edge->setAttribute('docker_compose_viz.type', 'volume');
+ $edge->setAttribute('graphviz.style', 'dashed');
+
+ if (false === ($definition['read_only'] ?? false)) {
+ $edge->setAttribute('graphviz.dir', 'both');
+ }
+
+ if (isset($definition['target'])) {
+ $edge->setAttribute('graphviz.label', $definition['target']);
+ }
+}
+
+/**
+ * @see https://github.com/compose-spec/compose-spec/blob/master/spec.md#volumes
+ */
+function normalizeVolumeMapping(string|array $mapping, array $volumes): array
+{
+ if (is_array($mapping)) {
+ return $mapping;
+ }
+
+ $parts = explode(':', $mapping);
+ $parts[1] = $parts[1] ?? $parts[0];
+ $parts[2] = explode(', ', $parts[2] ?? 'rw');
+
+ $type = in_array($parts[0], $volumes, true) ? 'volume' : 'bind';
+
+ return [
+ 'type' => $type,
+ 'source' => $parts[0],
+ 'target' => $parts[1],
+ 'read_only' => in_array('ro', $parts[2], true),
+ ];
+}
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php
new file mode 100644
index 0000000..1edab40
--- /dev/null
+++ b/tests/ConfigTest.php
@@ -0,0 +1,341 @@
+assertEquals([], fetchConfigs([]));
+ }
+
+ /**
+ * @test
+ */
+ public function returnConfigurationsFromDockerComposeConfiguration(): void
+ {
+ $configuration = [
+ 'configs' => [
+ 'test-config' => ['external' => true]
+ ]
+ ];
+
+ $this->assertEquals($configuration['configs'], fetchConfigs($configuration));
+ }
+
+ /**
+ * @test
+ */
+ public function generateVertexId(): void
+ {
+ $config = 'test-config';
+
+ $this->assertEquals('config:'.$config, getConfigVertexId($config));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $config = 'test-config';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue(new Vertex($graph, getConfigVertexId($config))));
+
+ addConfig($graph, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $config = 'test-config';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue(new Vertex($graph, getConfigVertexId($config))));
+
+ addConfig($graph, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $config = 'test-config';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('config')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($config)],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('note')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getConfigVertexId($config))
+ ->will($this->returnValue($vertex));
+
+ addConfig($graph, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ addConfigRelation(new Graph(), 'unknown', 'test-config');
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownConfiguration(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addConfigRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(): void
+ {
+ $config = 'test-config';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $configVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getConfigVertexId($config))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($configVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($configVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('config'));
+
+ addConfigRelation($graph, $service, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(): void
+ {
+ $config = 'test-config';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $configVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getConfigVertexId($config))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($configVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($configVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ addConfigRelation($graph, $service, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistInGraph(): void
+ {
+ $config = 'test-config';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $configVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getConfigVertexId($config))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($configVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ addConfigRelation($graph, $service, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAdding(): void
+ {
+ $config = 'test-config';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $configVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getConfigVertexId($config))]
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($configVertex),
+ )
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->once())
+ ->method('setAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'), $this->equalTo('config'));
+
+ addConfigRelation($graph, $service, $config);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAddingWithMapping(): void
+ {
+ $config = 'test-config';
+ $service = 'test-service';
+ $mapping = ['target' => 'config-target'];
+ $graph = $this->createMock(Graph::class);
+ $configVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getConfigVertexId($config))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($configVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($configVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(2))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('config')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($mapping['target'])],
+ );
+
+ addConfigRelation($graph, $service, $config, $mapping);
+ }
+}
diff --git a/tests/NetworkTest.php b/tests/NetworkTest.php
new file mode 100644
index 0000000..3b3388a
--- /dev/null
+++ b/tests/NetworkTest.php
@@ -0,0 +1,369 @@
+assertEquals([], fetchNetworks([]));
+ }
+
+ /**
+ * @test
+ */
+ public function returnNetworksFromDockerComposeConfiguration(): void
+ {
+ $configuration = [
+ 'networks' => [
+ 'test-net' => ['external' => true]
+ ]
+ ];
+
+ $this->assertEquals($configuration['networks'], fetchNetworks($configuration));
+ }
+
+ /**
+ * @test
+ */
+ public function generateVertexId(): void
+ {
+ $network = 'test-net';
+
+ $this->assertEquals('net:'.$network, getNetworkVertexId($network));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $network = 'test-net';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(new Vertex($graph, getNetworkVertexId($network))));
+
+ addNetwork($graph, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $network = 'test-net';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(new Vertex($graph, getNetworkVertexId($network))));
+
+ addNetwork($graph, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $network = 'test-net';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('network')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($network)],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('pentagon')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue($vertex));
+
+ addNetwork($graph, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexLabelAttributeFromName(): void
+ {
+ $network = 'test-net';
+ $definition = ['name' => 'test-net-name'];
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('network')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($definition['name'])],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('pentagon')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getNetworkVertexId($network))
+ ->will($this->returnValue($vertex));
+
+ addNetwork($graph, $network, $definition);
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ addNetworkRelation(new Graph(), 'unknown', 'test-net');
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownNetwork(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addNetworkRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(): void
+ {
+ $network = 'test-net';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $networkVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getNetworkVertexId($network))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($networkVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($networkVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('network'));
+
+ addNetworkRelation($graph, $service, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(): void
+ {
+ $network = 'test-net';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $networkVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getNetworkVertexId($network))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($networkVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($networkVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ addNetworkRelation($graph, $service, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistInGraph(): void
+ {
+ $network = 'test-net';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $networkVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getNetworkVertexId($network))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($networkVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ addNetworkRelation($graph, $service, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAdding(): void
+ {
+ $network = 'test-net';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $networkVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getNetworkVertexId($network))]
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($networkVertex),
+ )
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->once())
+ ->method('setAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'), $this->equalTo('network'));
+
+ addNetworkRelation($graph, $service, $network);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAddingWithMapping(): void
+ {
+ $network = 'test-net';
+ $service = 'test-service';
+ $mapping = ['aliases' => ['foo', 'bar']];
+ $graph = $this->createMock(Graph::class);
+ $networkVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getNetworkVertexId($network))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($networkVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($networkVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(2))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('network')],
+ [$this->equalTo('graphviz.label'), $this->equalTo(implode(', ', $mapping['aliases']))],
+ );
+
+ addNetworkRelation($graph, $service, $network, $mapping);
+ }
+}
diff --git a/tests/PortTest.php b/tests/PortTest.php
new file mode 100644
index 0000000..359ad06
--- /dev/null
+++ b/tests/PortTest.php
@@ -0,0 +1,298 @@
+assertEquals('port:'.$port, getPortVertexId($port));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $port = '443';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue(new Vertex($graph, getPortVertexId($port))));
+
+ addPort($graph, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $port = '443';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue(new Vertex($graph, getPortVertexId($port))));
+
+ addPort($graph, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $port = '443';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('port')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($port)],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('circle')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getPortVertexId($port))
+ ->will($this->returnValue($vertex));
+
+ addPort($graph, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ addPortRelation(new Graph(), 'unknown', normalizePortMapping('443'));
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownPort(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addPortRelation(new Graph(), $service, normalizePortMapping('443'));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(): void
+ {
+ $port = '443';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $portVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with($this->equalTo(getPortVertexId($port)))
+ ->will($this->returnValue(true));
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getPortVertexId($port))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($portVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($portVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('port'));
+
+ addPortRelation($graph, $service, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(): void
+ {
+ $port = '443';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $portVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with($this->equalTo(getPortVertexId($port)))
+ ->will($this->returnValue(true));
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getPortVertexId($port))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($portVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($portVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ addPortRelation($graph, $service, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistInGraph(): void
+ {
+ $port = '443';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $portVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with($this->equalTo(getPortVertexId($port)))
+ ->will($this->returnValue(true));
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getPortVertexId($port))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($portVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ addPortRelation($graph, $service, normalizePortMapping($port));
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAdding(): void
+ {
+ $port = '443';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $portVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with($this->equalTo(getPortVertexId($port)))
+ ->will($this->returnValue(true));
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getPortVertexId($port))]
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($portVertex),
+ )
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($portVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('port')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('solid')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($port)],
+ );
+
+ addPortRelation($graph, $service, normalizePortMapping($port));
+ }
+}
diff --git a/tests/SecretTest.php b/tests/SecretTest.php
new file mode 100644
index 0000000..4e7618d
--- /dev/null
+++ b/tests/SecretTest.php
@@ -0,0 +1,340 @@
+assertEquals([], fetchSecrets([]));
+ }
+
+ /**
+ * @test
+ */
+ public function returnSecreturationsFromDockerComposeSecreturation(): void
+ {
+ $secreturation = [
+ 'secrets' => [
+ 'test-secret' => ['external' => true]
+ ]
+ ];
+
+ $this->assertEquals($secreturation['secrets'], fetchSecrets($secreturation));
+ }
+
+ /**
+ * @test
+ */
+ public function generateVertexId(): void
+ {
+ $secret = 'test-secret';
+
+ $this->assertEquals('secret:'.$secret, getSecretVertexId($secret));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $secret = 'test-secret';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue(new Vertex($graph, getSecretVertexId($secret))));
+
+ addSecret($graph, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $secret = 'test-secret';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue(new Vertex($graph, getSecretVertexId($secret))));
+
+ addSecret($graph, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $secret = 'test-secret';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(2))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('secret')],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('hexagon')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getSecretVertexId($secret))
+ ->will($this->returnValue($vertex));
+
+ addSecret($graph, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ addSecretRelation(new Graph(), 'unknown', 'test-secret');
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownSecreturation(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addSecretRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(): void
+ {
+ $secret = 'test-secret';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $secretVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getSecretVertexId($secret))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($secretVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($secretVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('secret'));
+
+ addSecretRelation($graph, $service, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(): void
+ {
+ $secret = 'test-secret';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $secretVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getSecretVertexId($secret))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($secretVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($secretVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ addSecretRelation($graph, $service, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistInGraph(): void
+ {
+ $secret = 'test-secret';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $secretVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getSecretVertexId($secret))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($secretVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ addSecretRelation($graph, $service, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAdding(): void
+ {
+ $secret = 'test-secret';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $secretVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getSecretVertexId($secret))]
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($secretVertex),
+ )
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->once())
+ ->method('setAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'), $this->equalTo('secret'));
+
+ addSecretRelation($graph, $service, $secret);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAddingWithMapping(): void
+ {
+ $secret = 'test-secret';
+ $service = 'test-service';
+ $mapping = ['source' => $secret, 'target' => 'secret-target'];
+ $graph = $this->createMock(Graph::class);
+ $secretVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getSecretVertexId($secret))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($secretVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($secretVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(2))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('secret')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($mapping['target'])],
+ );
+
+ addSecretRelation($graph, $service, $mapping);
+ }
+}
diff --git a/tests/ServiceTest.php b/tests/ServiceTest.php
new file mode 100644
index 0000000..fc737f9
--- /dev/null
+++ b/tests/ServiceTest.php
@@ -0,0 +1,478 @@
+assertEquals([], fetchServices([]));
+ }
+
+ /**
+ * @test
+ */
+ public function returnServicesFromDockerComposeConfiguration(): void
+ {
+ $configuration = [
+ 'services' => [
+ 'test-service' => ['external' => true]
+ ]
+ ];
+
+ $this->assertEquals($configuration['services'], fetchServices($configuration));
+ }
+
+ /**
+ * @test
+ */
+ public function generateVertexId(): void
+ {
+ $service = 'test-service';
+
+ $this->assertEquals('service:'.$service, getServiceVertexId($service));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue(new Vertex($graph, getServiceVertexId($service))));
+
+ addService($graph, $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue(new Vertex($graph, getServiceVertexId($service))));
+
+ addService($graph, $service);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(2))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('service')],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('component')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getServiceVertexId($service))
+ ->will($this->returnValue($vertex));
+
+ addService($graph, $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addExtendsRelationWithUnknownSourceService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addExtendsRelation(new Graph(), 'unknown', $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addExtendsRelationWithUnknownTargetService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addExtendsRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function addLinkRelationWithUnknownSourceService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addLinkRelation(new Graph(), 'unknown', $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addLinkRelationWithUnknownTargetService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addLinkRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function addExternalLinkRelationWithUnknownSourceService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addExternalLinkRelation(new Graph(), 'unknown', $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addExternalLinkRelationWithUnknownTargetService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addExternalLinkRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function addDependsRelationWithUnknownSourceService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addDependsRelation(new Graph(), 'unknown', $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addDependsRelationWithUnknownTargetService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addDependsRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ */
+ public function addVolumesFromRelationWithUnknownSourceService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addVolumesFromRelation(new Graph(), 'unknown', $service);
+ }
+
+ /**
+ * @test
+ */
+ public function addVolumesFromRelationWithUnknownTargetService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addVolumesFromRelation(new Graph(), $service, 'unknown');
+ }
+
+ /**
+ * @test
+ * @dataProvider checkIfEdgeOfExpectedTypeAlreadyExistsInGraphProvider
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(string $type, callable $function): void
+ {
+ $source = 'source-service';
+ $target = 'target-service';
+ $graph = $this->createMock(Graph::class);
+ $sourceVertex = $this->createMock(Vertex::class);
+ $targetVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($source))],
+ [$this->equalTo(getServiceVertexId($target))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($sourceVertex),
+ $this->returnValue($targetVertex),
+ ),
+ );
+ $sourceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue(true));
+ $sourceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($targetVertex))
+ ->will($this->returnValue([$relation]));
+ $sourceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue($type));
+
+ $function($graph, $source, $target);
+ }
+
+ private function checkIfEdgeOfExpectedTypeAlreadyExistsInGraphProvider(): array
+ {
+ return [
+ 'extends' => ['extends', 'PMSIpilot\DockerComposeViz\addExtendsRelation'],
+ 'link' => ['link', 'PMSIpilot\DockerComposeViz\addLinkRelation'],
+ 'external_link' => ['external_link', 'PMSIpilot\DockerComposeViz\addExternalLinkRelation'],
+ 'depends' => ['depends', 'PMSIpilot\DockerComposeViz\addDependsRelation'],
+ 'volumes_from' => ['volumes_from', 'PMSIpilot\DockerComposeViz\addVolumesFromRelation'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider addEdgeIfNotExistsProvider
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(callable $function): void
+ {
+ $source = 'source-service';
+ $target = 'target-service';
+ $graph = $this->createMock(Graph::class);
+ $sourceVertex = $this->createMock(Vertex::class);
+ $targetVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($source))],
+ [$this->equalTo(getServiceVertexId($target))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($sourceVertex),
+ $this->returnValue($targetVertex),
+ ),
+ );
+ $sourceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue(true));
+ $sourceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($targetVertex))
+ ->will($this->returnValue([$relation]));
+ $sourceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ $function($graph, $source, $target);
+ }
+
+ /**
+ * @test
+ * @dataProvider addEdgeIfNotExistsProvider
+ */
+ public function addEdgeIfNotExistInGraph(callable $function): void
+ {
+ $source = 'source-service';
+ $target = 'target-service';
+ $graph = $this->createMock(Graph::class);
+ $sourceVertex = $this->createMock(Vertex::class);
+ $targetVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($source))],
+ [$this->equalTo(getServiceVertexId($target))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($sourceVertex),
+ $this->returnValue($targetVertex),
+ ),
+ );
+ $sourceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue(false));
+ $sourceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ $function($graph, $source, $target);
+ }
+
+ private function addEdgeIfNotExistsProvider(): array
+ {
+ return [
+ 'extends' => ['PMSIpilot\DockerComposeViz\addExtendsRelation'],
+ 'link' => ['PMSIpilot\DockerComposeViz\addLinkRelation'],
+ 'external_link' => ['PMSIpilot\DockerComposeViz\addExternalLinkRelation'],
+ 'depends' => ['PMSIpilot\DockerComposeViz\addDependsRelation'],
+ 'volumes_from' => ['PMSIpilot\DockerComposeViz\addVolumesFromRelation'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider setEdgeAttributesWhenAddingProvider
+ */
+ public function setEdgeAttributesWhenAdding(callable $function, array ...$setAttributesCalls): void
+ {
+ $source = 'source-service';
+ $target = 'target-service';
+ $graph = $this->createMock(Graph::class);
+ $sourceVertex = $this->createMock(Vertex::class);
+ $targetVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($source))],
+ [$this->equalTo(getServiceVertexId($target))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($sourceVertex),
+ $this->returnValue($targetVertex),
+ ),
+ );
+ $sourceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue(false));
+ $sourceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($targetVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(count($setAttributesCalls)))
+ ->method('setAttribute')
+ ->withConsecutive(...$setAttributesCalls);
+
+ $function($graph, $source, $target);
+ }
+
+ private function setEdgeAttributesWhenAddingProvider(): array
+ {
+ return [
+ 'extends' => [
+ 'PMSIpilot\DockerComposeViz\addExtendsRelation',
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('extends')],
+ [$this->equalTo('graphviz.dir'), $this->equalTo('both')],
+ [$this->equalTo('graphviz.arrowhead'), $this->equalTo('inv')],
+ [$this->equalTo('graphviz.arrowtail'), $this->equalTo('dot')],
+ ],
+ 'link' => [
+ 'PMSIpilot\DockerComposeViz\addLinkRelation',
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('link')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('solid')],
+ ],
+ 'external_link' => [
+ 'PMSIpilot\DockerComposeViz\addExternalLinkRelation',
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('external_link')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('solid')],
+ [$this->equalTo('graphviz.color'), $this->equalTo('gray')],
+ ],
+ 'depends' => [
+ 'PMSIpilot\DockerComposeViz\addDependsRelation',
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('depends')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('dotted')],
+ ],
+ 'volumes_from' => [
+ 'PMSIpilot\DockerComposeViz\addVolumesFromRelation',
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('volumes_from')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('dashed')],
+ ],
+ ];
+ }
+}
diff --git a/tests/VolumeTest.php b/tests/VolumeTest.php
new file mode 100644
index 0000000..7b5f108
--- /dev/null
+++ b/tests/VolumeTest.php
@@ -0,0 +1,347 @@
+assertEquals([], fetchVolumes([]));
+ }
+
+ /**
+ * @test
+ */
+ public function returnVolumesFromDockerComposeConfiguration(): void
+ {
+ $configuration = [
+ 'volumes' => [
+ 'test-volume' => ['external' => true]
+ ]
+ ];
+
+ $this->assertEquals($configuration['volumes'], fetchVolumes($configuration));
+ }
+
+ /**
+ * @test
+ */
+ public function generateVertexId(): void
+ {
+ $volume = 'test-volume';
+
+ $this->assertEquals('volume:'.$volume, getVolumeVertexId($volume));
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfVertexAlreadyExistsInGraph(): void
+ {
+ $volume = 'test-volume';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue(true));
+ $graph->expects($this->once())
+ ->method('getVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue(new Vertex($graph, getVolumeVertexId($volume))));
+
+ addVolume($graph, $volume);
+ }
+
+ /**
+ * @test
+ */
+ public function addVertexIfNotExistInGraph(): void
+ {
+ $volume = 'test-volume';
+ $graph = $this->createMock(Graph::class);
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue(new Vertex($graph, getVolumeVertexId($volume))));
+
+ addVolume($graph, $volume);
+ }
+
+ /**
+ * @test
+ */
+ public function setVertexAttributesWhenAdding(): void
+ {
+ $volume = 'test-volume';
+ $graph = $this->createMock(Graph::class);
+ $vertex = $this->createMock(Vertex::class);
+ $vertex->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('volume')],
+ [$this->equalTo('graphviz.label'), $this->equalTo($volume)],
+ [$this->equalTo('graphviz.shape'), $this->equalTo('pentagon')],
+ );
+ $graph->expects($this->once())
+ ->method('hasVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue(false));
+ $graph->expects($this->once())
+ ->method('createVertex')
+ ->with(getVolumeVertexId($volume))
+ ->will($this->returnValue($vertex));
+
+ addVolume($graph, $volume);
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownService(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ addVolumeRelation(new Graph(), 'unknown', ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function addRelationWithUnknownVolume(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+
+ $service = 'test-service';
+ $graph = new Graph();
+ $graph->createVertex($service);
+
+ addVolumeRelation(new Graph(), $service, ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfEdgeOfExpectedTypeAlreadyExistsInGraph(): void
+ {
+ $volume = 'test-volume';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $volumeVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getVolumeVertexId($volume))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($volumeVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($volumeVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->never()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('volume'));
+
+ addVolumeRelation($graph, $service, ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistWithExpectedTypeInGraph(): void
+ {
+ $volume = 'test-volume';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $volumeVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getVolumeVertexId($volume))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($volumeVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue(true));
+ $serviceVertex->expects($this->once())
+ ->method('getEdgesTo')
+ ->with($this->equalTo($volumeVertex))
+ ->will($this->returnValue([$relation]));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+ $relation->expects($this->once())
+ ->method('getAttribute')
+ ->with($this->equalTo('docker_compose_viz.type'))
+ ->will($this->returnValue('unknown'));
+
+ addVolumeRelation($graph, $service, ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function addEdgeIfNotExistInGraph(): void
+ {
+ $volume = 'test-volume';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $volumeVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getVolumeVertexId($volume))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($volumeVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue($this->createMock(Directed::class)));
+
+ addVolumeRelation($graph, $service, ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAdding(): void
+ {
+ $volume = 'test-volume';
+ $service = 'test-service';
+ $graph = $this->createMock(Graph::class);
+ $volumeVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getVolumeVertexId($volume))]
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($volumeVertex),
+ )
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(3))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('volume')],
+ [$this->equalTo('graphviz.style'), $this->equalTo('dashed')],
+ [$this->equalTo('graphviz.dir'), $this->equalTo('both')],
+ );
+
+ addVolumeRelation($graph, $service, ['source' => 'test-volume']);
+ }
+
+ /**
+ * @test
+ */
+ public function setEdgeAttributesWhenAddingWithDefinition(): void
+ {
+ $volume = 'test-volume';
+ $service = 'test-service';
+ $definition = ['source' => $volume, 'target' => '/target'];
+ $graph = $this->createMock(Graph::class);
+ $volumeVertex = $this->createMock(Vertex::class);
+ $serviceVertex = $this->createMock(Vertex::class);
+ $relation = $this->createMock(Directed::class);
+ $graph->expects($this->exactly(2))
+ ->method('getVertex')
+ ->withConsecutive(
+ [$this->equalTo(getServiceVertexId($service))],
+ [$this->equalTo(getVolumeVertexId($volume))],
+ )
+ ->will(
+ $this->onConsecutiveCalls(
+ $this->returnValue($serviceVertex),
+ $this->returnValue($volumeVertex),
+ ),
+ );
+ $serviceVertex->expects($this->once())
+ ->method('hasEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue(false));
+ $serviceVertex->expects(($this->once()))
+ ->method('createEdgeTo')
+ ->with($this->identicalTo($volumeVertex))
+ ->will($this->returnValue($relation));
+ $relation->expects($this->exactly(4))
+ ->method('setAttribute')
+ ->withConsecutive(
+ [$this->equalTo('docker_compose_viz.type'), $this->equalTo('volume')],
+ [$this->equalTo('graphviz.style'), 'dashed'],
+ [$this->equalTo('graphviz.dir'), $this->equalTo('both')],
+ [$this->equalTo('graphviz.label'), $definition['target']],
+ );
+
+ addVolumeRelation($graph, $service, $definition);
+ }
+}