This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

2
vendor/graphp/graphviz/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/vendor
/composer.lock

34
vendor/graphp/graphviz/.travis.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
language: php
php:
# - 5.3 # requires old distro, see below
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- hhvm # ignore errors, see below
# lock distro so future defaults will not break the build
dist: trusty
matrix:
include:
- php: 5.3
dist: precise
allow_failures:
- php: hhvm
sudo: false
addons:
apt:
packages:
- graphviz
install:
- composer install --no-interaction
script:
- vendor/bin/phpunit --coverage-text

44
vendor/graphp/graphviz/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,44 @@
# Changelog
## 0.2.2 (2019-10-04)
* Feature: Omit root graph name unless explicitly assigned via `graphviz.name` attribute.
(#28 by @rhelms and @clue)
```php
$graph = new Graph();
$graph->setAttribute('graphviz.name', 'g');
```
* Feature: Remove unneeded dependency on `graphp/algorithms`.
(#39 by @clue)
* Feature / Fix: Use UTF-8 encoding (Unicode) by default and respect charset attribute.
(#27 by @Ithilias and @clue)
* Fix: Fix representing directed loop edges as directed edge
(#37 by @clue)
* Add examples and documentation for GraphViz attributes, labels and record shapes.
(#26 by @clue)
* Update test suite to support PHPUnit 6 and PHPUnit 5 and support running on legacy PHP 5.3 through PHP 7.2 and HHVM.
(#24 by @clue)
## 0.2.1 (2015-03-08)
* Support graph v0.9 (while keeping BC)
([#9](https://github.com/graphp/graphviz/pull/9))
## 0.2.0 (2015-01-19)
* BC break: Refactor to inject Graph into GraphViz on demand, inject GraphViz into exporters
([#6](https://github.com/graphp/graphviz/pull/6))
* BC break: Remove legacy layout helper
([#5](https://github.com/graphp/graphviz/pull/5))
## 0.1.0 (2014-12-31)
* First tagged release, split off from [clue/graph](https://github.com/clue/graph) v0.8.0
([#1](https://github.com/graphp/graphviz/issues/1))

22
vendor/graphp/graphviz/LICENSE vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2012+ Christian Lück (Maintainer)
Copyright (c) 2012+ Fhaculty Core Team and our awesome contributors <https://github.com/clue/graph/graphs/contributors>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

425
vendor/graphp/graphviz/README.md vendored Normal file
View File

@@ -0,0 +1,425 @@
# graphp/graphviz [![Build Status](https://travis-ci.org/graphp/graphviz.svg?branch=master)](https://travis-ci.org/graphp/graphviz)
GraphViz graph drawing for the mathematical graph/network library GraPHP.
The library supports visualizing graph images, including them into webpages,
opening up images from within CLI applications and exporting them
as PNG, JPEG or SVG file formats (among many others).
Because [graph drawing](http://en.wikipedia.org/wiki/Graph_drawing) is a complex area on its own,
the actual layouting of the graph is left up to the excelent [GraphViz](http://www.graphviz.org/)
"Graph Visualization Software" and we merely provide some convenient APIs to interface with GraphViz.
> Note: This project is in beta stage! Feel free to report any issues you encounter.
**Table of contents**
* [Quickstart examples](#quickstart-examples)
* [Attributes](#attributes)
* [Graph attributes](#graph-attributes)
* [Vertex attributes](#vertex-attributes)
* [Edge attributes](#edge-attributes)
* [Labels](#labels)
* [Vertex labels](#vertex-labels)
* [Edge labels](#edge-labels)
* [HTML-like labels](#html-like-labels)
* [Record-based nodes](#record-based-nodes)
* [Install](#install)
* [Tests](#tests)
* [License](#license)
## Quickstart examples
Once [installed](#install), let's build and display a sample graph:
````php
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
$blue->setAttribute('graphviz.color', 'blue');
$red = $graph->createVertex('red');
$red->setAttribute('graphviz.color', 'red');
$edge = $blue->createEdgeTo($red);
$edge->setAttribute('graphviz.color', 'grey');
$graphviz = new Graphp\GraphViz\GraphViz();
$graphviz->display($graph);
````
The above code will open your default image viewer with the following image:
![red-blue](examples/01-simple.png)
See also the [examples](examples/).
## Attributes
GraphViz supports a number of attributes on the graph instance itself, each
vertex instance (GraphViz calls these "nodes") and edge instance. Any of these
GraphViz attributes are supported by this library and have to be assigned using
GraPHP attributes as documented below.
For the full list of all GraphViz attributes, please refer to the
[GraphViz documentation](https://graphviz.gitlab.io/_pages/doc/info/attrs.html).
Note that all attributes use UTF-8 encoding (Unicode) and will be quoted and
escaped by default, so a `ö` and `>` will appear as-is and will not be
interpreted as HTML. See also [HTML-like labels](#html-like-labels) below for
more details.
### Graph attributes
GraphViz supports a number of attributes on the graph instance itself. Any of
these GraphViz attributes are supported by this library and have to be assigned
on the graph instance with the `graphviz.graph.` prefix like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.graph.bgcolor', 'transparent');
```
> Note how this uses the `graphviz.graph.` prefix and not just `graphviz.`. This
is done for consistency reasons with respect to default vertex and edge
attributes as documented below.
For example, the `rankdir` attribute can be used to change the orientation to
horizontal mode (left to right) like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.graph.rankdir', 'LR');
$hello = $graph->createVertex('hello');
$world = $graph->createVertex('wörld');
$hello->createEdgeTo($world);
```
![html graph example](examples/02-html.png)
See also the [examples](examples/).
Additionally, this library accepts an optional `graphviz.name` attribute that
will be used as the name (or ID) for the root graph object in the DOT output if
given. Unless explicitly assigned, this will be omitted by default. It is common
to assign a `G` here, but usually there should be no need to assign this. Among
others, this may be used as the title or tooltip in SVG output.
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.name', 'G');
$graph->createVertex('first');
```
### Vertex attributes
GraphViz supports a number of attributes on each vertex instance (GraphViz calls
these "node" attributes). Any of these GraphViz attributes are supported by this
library and have to be assigned on the respective vertex instance with the
`graphviz.` prefix like this:
```php
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
$blue->setAttribute('graphviz.color', 'blue');
```
Additionally, GraphViz also supports default attributes for all vertices. Any of
these GraphViz attributes are supported by this library and have to be assigned
on the graph instance with the `graphviz.node.` prefix like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.node.color', 'grey');
$grey = $graph->createVertex('grey');
```
These default attributes can be overriden on each vertex instance by explicitly
assigning the same attribute on the respective vertex instance like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.node.color', 'grey');
$blue = $graph->createVertex('blue');
$blue->setAttribute('graphviz.color', 'blue');
```
> Note how this uses the `graphviz.node.` prefix and not `graphviz.vertex.`. This
is done for consistency reasons with respect to how GraphViz assigns these
default attributes in its DOT output.
### Edge attributes
GraphViz supports a number of attributes on each edge instance. Any of these
GraphViz attributes are supported by this library and have to be assigned on the
respective edge instance with the `graphviz.` prefix like this:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$blue = $a->createEdgeTo($b);
$blue->setAttribute('graphviz.color', 'blue');
```
Additionally, GraphViz also supports default attributes for all edges. Any of
these GraphViz attributes are supported by this library and have to be assigned
on the graph instance with the `graphviz.edge.` prefix like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.edge.color', 'grey');
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$grey = $a->createEdgeTo($b);
```
These default attributes can be overriden on each edge instance by explicitly
assigning the same attribute on the respective edge instance like this:
```php
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.edge.color', 'grey');
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$blue = $a->createEdgeTo($b);
$blue->setAttribute('graphviz.color', 'blue');
```
## Labels
### Vertex labels
By default, GraphViz will always render the vertex ID as the label:
```php
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
```
If you assign a vertex balance, this library will automatically include a
`label` attribute that includes the balance value. The following example will
automatically assign `blue (+10)` as the label:
```php
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
$blue->setBalance(10);
```
You can use [vertex attributes](#vertex-attributes) to explicitly assign a
custom `label` attribute. Note that any balance value will still be appended
like in the previous example.
```php
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
$blue->setAttribute('graphviz.label', 'Hello world!');
```
Note that all [attributes](#attributes) will be quoted and escaped by default,
so a `>` will appear as-is and will not be interpreted as HTML. See also
[HTML-like labels](#html-like-labels) below for more details.
### Edge labels
By default, GraphViz will not render any label on an edge:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$edge = $a->createEdgeTo($b);
```
If you assign an edge flow, capacity or weight, this library will automatically
include a `label` attribute that includes these values. The following example
will automatically assign `100` as the label for the weighted edge:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$edge = $a->createEdgeTo($b);
$edge->setWeight(100);
```
The following example will automatically assign `4/10` as the label for an edge
with both flow and maximum capacity set:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$edge = $a->createEdgeTo($b);
$edge->setFlow(4);
$edge->setCapacity(10);
```
The following example will automatically assign `4/∞/100` as the label for a
weighted edge with a flow and unlimited capacity:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$edge = $a->createEdgeTo($b);
$edge->setFlow(4);
$edge->setCapacity(null);
$edge->setWeight(100);
```
You can use [edge attributes](#edge-attributes) to explicitly assign any
custom `label` attribute. Note that any flow, capacity or weight value will still
be appended like in the previous examples.
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('a');
$b = $graph->createVertex('b');
$edge = $a->createEdgeTo($b);
$edge->setAttribute('graphviz.label', 'important');
```
### HTML-like labels
Note that all [attributes](#attributes) will be quoted and escaped by default,
so a `>` will appear as-is and will not be interpreted as HTML. GraphViz also
supports [HTML-like labels](https://graphviz.gitlab.io/_pages/doc/info/shapes.html#html)
that support a subset of HTML features.
GraphViz requires any HTML-like label to be wrapped in `<` and `>` and only
supports a limited subset of HTML features as documented above. In order to
prevent automatic quoting and escaping, all attribute values have to be passed
to the static `GraphViz::raw()` helper like this:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('Entity');
$a->setAttribute('graphviz.shape', 'none');
$a->setAttribute('graphviz.label', GraphViz::raw('<
<table cellspacing="0" border="0" cellborder="1">
<tr><td bgcolor="#eeeeee"><b>\N</b></td></tr>
<tr><td></td></tr><tr>
<td>+ touch()</td></tr>
</table>>'));
$b = $graph->createVertex('Block');
$b->createEdgeTo($a);
$b->setAttribute('graphviz.shape', 'none');
$b->setAttribute('graphviz.label', GraphViz::raw('<
<table cellspacing="0" border="0" cellborder="1">
<tr><td bgcolor="#eeeeee"><b>\N</b></td></tr>
<tr><td>- size:int</td></tr>
<tr><td>+ touch()</td></tr>
</table>>'));
```
![UML html graph example](examples/11-uml-html.png)
See also the [examples](examples/).
### Record-based nodes
Note that all [attributes](#attributes) will be quoted and escaped by default,
so a `>` will appear as-is and will not be interpreted as HTML. Similar to the
above [HTML-like labels](#html-like-labels), GraphViz also supports simple
[record-based nodes](https://graphviz.gitlab.io/_pages/doc/info/shapes.html#record)
using the `record` and `Mrecord` shape attributes and structured label attributes.
GraphViz requires any record-based node label to be quoted, but uses special
syntax to mark record fields and optional port names. In order to prevent
automatic quoting and escaping, all attribute values have to be quoted manually
and passed to the static `GraphViz::raw()` helper like this:
```php
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex();
$a->setAttribute('graphviz.shape', 'Mrecord');
$a->setAttribute('graphviz.label', GraphViz::raw('"<f0> left |<middle> middle |<f2> right"'));
$b = $graph->createVertex();
$b->setAttribute('graphviz.shape', 'Mrecord');
$b->setAttribute('graphviz.label', GraphViz::raw('"<f0> left |<f1> middle |<right> right"'));
// a:middle -> b:right
$edge = $a->createEdgeTo($b);
$edge->setAttribute('graphviz.tailport', 'middle');
$edge->setAttribute('graphviz.headport', 'right');
```
![records with ports graph example](examples/13-record-ports.png)
See also the [examples](examples/).
## Install
The recommended way to install this library is [through Composer](https://getcomposer.org).
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
This will install the latest supported version:
```bash
$ composer require graphp/graphviz:^0.2.2
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
This project aims to run on any platform and thus does not require any PHP
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
HHVM.
It's *highly recommended to use PHP 7+* for this project.
The graph drawing feature is powered by the excellent [GraphViz](https://www.graphviz.org)
software. This means you'll have to install GraphViz (`dot` executable).
The [Graphviz homepage](https://www.graphviz.org/download/) includes complete
installation instructions for most common platforms, users of Debian/Ubuntu-based
distributions may simply invoke:
```bash
$ sudo apt install graphviz
```
## Tests
To run the test suite, you first need to clone this repo and then install all
dependencies [through Composer](https://getcomposer.org):
```bash
$ composer install
```
To run the test suite, go to the project root and run:
```bash
$ php vendor/bin/phpunit
```
## License
Released under the terms of the permissive [MIT license](http://opensource.org/licenses/MIT).

18
vendor/graphp/graphviz/composer.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "graphp/graphviz",
"type": "library",
"description": "GraphViz graph drawing for the mathematical graph/network library GraPHP.",
"keywords": ["GraphViz", "graph drawing", "graph image", "dot output", "GraPHP"],
"homepage": "https://github.com/graphp/graphviz",
"license": "MIT",
"autoload": {
"psr-4": {"Graphp\\GraphViz\\": "src/"}
},
"require": {
"php": ">=5.3.0",
"clue/graph": "~0.9.0|~0.8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
}
}

View File

@@ -0,0 +1,17 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$graph = new Fhaculty\Graph\Graph();
$blue = $graph->createVertex('blue');
$blue->setAttribute('graphviz.color', 'blue');
$red = $graph->createVertex('red');
$red->setAttribute('graphviz.color', 'red');
$edge = $blue->createEdgeTo($red);
$edge->setAttribute('graphviz.color', 'grey');
$graphviz = new Graphp\GraphViz\GraphViz();
$graphviz->display($graph);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,25 @@
<?php
// $ php -S localhost:8080 examples/02-html.php
require __DIR__ . '/../vendor/autoload.php';
$graph = new Fhaculty\Graph\Graph();
$graph->setAttribute('graphviz.graph.rankdir', 'LR');
$hello = $graph->createVertex('hello');
$world = $graph->createVertex('wörld');
$hello->createEdgeTo($world);
$graphviz = new Graphp\GraphViz\GraphViz();
$graphviz->setFormat('svg');
echo '<!DOCTYPE html>
<html>
<head>
<title>hello wörld</title>
<body>
' . $graphviz->createImageHtml($graph) . '
</body>
</html>
';

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -0,0 +1,30 @@
<?php
use Graphp\GraphViz\GraphViz;
require __DIR__ . '/../vendor/autoload.php';
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('Entity');
$a->setAttribute('graphviz.shape', 'none');
$a->setAttribute('graphviz.label', GraphViz::raw('<
<table cellspacing="0" border="0" cellborder="1">
<tr><td bgcolor="#eeeeee"><b>\N</b></td></tr>
<tr><td></td></tr><tr>
<td>+ touch()</td></tr>
</table>>'));
$b = $graph->createVertex('Block');
$b->createEdgeTo($a);
$b->setAttribute('graphviz.shape', 'none');
$b->setAttribute('graphviz.label', GraphViz::raw('<
<table cellspacing="0" border="0" cellborder="1">
<tr><td bgcolor="#eeeeee"><b>\N</b></td></tr>
<tr><td>- size:int</td></tr>
<tr><td>+ touch()</td></tr>
</table>>'));
$graphviz = new GraphViz();
echo $graphviz->createScript($graph);
$graphviz->display($graph);

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,20 @@
<?php
use Graphp\GraphViz\GraphViz;
require __DIR__ . '/../vendor/autoload.php';
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex('Entity');
$a->setAttribute('graphviz.shape', 'record');
$a->setAttribute('graphviz.label', GraphViz::raw('"{\N||+ touch()}"'));
$b = $graph->createVertex('Block');
$b->createEdgeTo($a);
$b->setAttribute('graphviz.shape', 'record');
$b->setAttribute('graphviz.label', GraphViz::raw('"{\N|- size:int|+ touch()}"'));
$graphviz = new GraphViz();
echo $graphviz->createScript($graph);
$graphviz->display($graph);

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,24 @@
<?php
use Graphp\GraphViz\GraphViz;
require __DIR__ . '/../vendor/autoload.php';
$graph = new Fhaculty\Graph\Graph();
$a = $graph->createVertex();
$a->setAttribute('graphviz.shape', 'Mrecord');
$a->setAttribute('graphviz.label', GraphViz::raw('"<f0> left |<middle> middle |<f2> right"'));
$b = $graph->createVertex();
$b->setAttribute('graphviz.shape', 'Mrecord');
$b->setAttribute('graphviz.label', GraphViz::raw('"<f0> left |<f1> middle |<right> right"'));
// a:middle -> b:right
$edge = $a->createEdgeTo($b);
$edge->setAttribute('graphviz.tailport', 'middle');
$edge->setAttribute('graphviz.headport', 'right');
$graphviz = new GraphViz();
echo $graphviz->createScript($graph);
$graphviz->display($graph);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

19
vendor/graphp/graphviz/phpunit.xml.dist vendored Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite name="GraphViz Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit>

26
vendor/graphp/graphviz/src/Dot.php vendored Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace Graphp\GraphViz;
use Graphp\GraphViz\GraphViz;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Exporter\ExporterInterface;
class Dot implements ExporterInterface
{
private $graphviz;
public function __construct(GraphViz $graphviz = null)
{
if ($graphviz === null) {
$graphviz = new GraphViz();
}
$this->graphviz = $graphviz;
}
public function getOutput(Graph $graph)
{
return $this->graphviz->createScript($graph);
}
}

446
vendor/graphp/graphviz/src/GraphViz.php vendored Normal file
View File

@@ -0,0 +1,446 @@
<?php
namespace Graphp\GraphViz;
use Fhaculty\Graph\Attribute\AttributeBagNamespaced;
use Fhaculty\Graph\Edge\Base as Edge;
use Fhaculty\Graph\Edge\Directed as EdgeDirected;
use Fhaculty\Graph\Exception\UnexpectedValueException;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Vertex;
class GraphViz
{
/**
* file output format to use
*
* @var string
* @see GraphViz::setFormat()
*/
private $format = 'png';
/**
* Either the name of full path to GraphViz layout.
*
* @var string
* @see GraphViz::setExecutable()
*/
private $executable = 'dot';
/**
* string to use as indentation for dot output
*
* @var string
* @see GraphViz::createScript()
*/
private $formatIndent = ' ';
const DELAY_OPEN = 2.0;
const EOL = PHP_EOL;
public function __construct()
{
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$this->executable = 'dot.exe';
}
}
/**
* Change the executable to use.
*
* Usually, your graphviz executables should be located in your $PATH
* environment variable and invoking a mere `dot` is sufficient. If you
* have no access to your $PATH variable, use this method to set the path
* to your graphviz dot executable.
*
* This should contain '.exe' on windows.
* - /full/path/to/bin/dot
* - neato
* - dot.exe
* - c:\path\to\bin\dot.exe
*
* @param string $executable
* @return GraphViz $this (chainable)
*/
public function setExecutable($executable) {
$this->executable = $executable;
return $this;
}
/**
* return executable to use
*
* @return string
* @see GraphViz::setExecutable()
*/
public function getExecutable() {
return $this->executable;
}
/**
* set graph image output format
*
* @param string $format png, svg, ps2, etc. (see 'man dot' for details on parameter '-T')
* @return GraphViz $this (chainable)
*/
public function setFormat($format)
{
$this->format = $format;
return $this;
}
/**
* create and display image for this graph
*
* @param Graph $graph graph to display
* @return void
* @uses GraphViz::createImageFile()
*/
public function display(Graph $graph)
{
// echo "Generate picture ...";
$tmp = $this->createImageFile($graph);
static $next = 0;
if ($next > microtime(true)) {
// wait some time between calling xdg-open because earlier calls will be ignored otherwise
//echo '[delay flooding xdg-open]' . PHP_EOL;
sleep(self::DELAY_OPEN);
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// open image in untitled, temporary background shell
exec('start "" ' . escapeshellarg($tmp) . ' >NUL');
} elseif (strtoupper(PHP_OS) === 'DARWIN') {
// open image in background (redirect stdout to /dev/null, sterr to stdout and run in background)
exec('open ' . escapeshellarg($tmp) . ' > /dev/null 2>&1 &');
} else {
// open image in background (redirect stdout to /dev/null, sterr to stdout and run in background)
exec('xdg-open ' . escapeshellarg($tmp) . ' > /dev/null 2>&1 &');
}
$next = microtime(true) + self::DELAY_OPEN;
// echo "... done\n";
}
/**
* create image file data contents for this graph
*
* @param Graph $graph graph to display
* @return string
* @uses GraphViz::createImageFile()
*/
public function createImageData(Graph $graph)
{
$file = $this->createImageFile($graph);
$data = file_get_contents($file);
unlink($file);
return $data;
}
/**
* create base64-encoded image src target data to be used for html images
*
* @param Graph $graph graph to display
* @return string
* @uses GraphViz::createImageData()
*/
public function createImageSrc(Graph $graph)
{
$format = $this->format;
if ($this->format === 'svg' || $this->format === 'svgz') {
$format = 'svg+xml;charset=' . $graph->getAttribute('graphviz.graph.charset', 'UTF-8');
}
return 'data:image/' . $format . ';base64,' . base64_encode($this->createImageData($graph));
}
/**
* create image html code for this graph
*
* @param Graph $graph graph to display
* @return string
* @uses GraphViz::createImageSrc()
*/
public function createImageHtml(Graph $graph)
{
if ($this->format === 'svg' || $this->format === 'svgz') {
return '<object type="image/svg+xml" data="' . $this->createImageSrc($graph) . '"></object>';
}
return '<img src="' . $this->createImageSrc($graph) . '" />';
}
/**
* create image file for this graph
*
* @param Graph $graph graph to display
* @return string filename
* @throws UnexpectedValueException on error
* @uses GraphViz::createScript()
*/
public function createImageFile(Graph $graph)
{
$script = $this->createScript($graph);
// var_dump($script);
$tmp = tempnam(sys_get_temp_dir(), 'graphviz');
if ($tmp === false) {
throw new UnexpectedValueException('Unable to get temporary file name for graphviz script');
}
$ret = file_put_contents($tmp, $script, LOCK_EX);
if ($ret === false) {
throw new UnexpectedValueException('Unable to write graphviz script to temporary file');
}
$ret = 0;
$executable = $this->getExecutable();
system(escapeshellarg($executable) . ' -T ' . escapeshellarg($this->format) . ' ' . escapeshellarg($tmp) . ' -o ' . escapeshellarg($tmp . '.' . $this->format), $ret);
if ($ret !== 0) {
throw new UnexpectedValueException('Unable to invoke "' . $executable .'" to create image file (code ' . $ret . ')');
}
unlink($tmp);
return $tmp . '.' . $this->format;
}
/**
* create graphviz script representing this graph
*
* @param Graph $graph graph to display
* @return string
* @uses Directed::hasDirected()
* @uses Graph::getVertices()
* @uses Graph::getEdges()
*/
public function createScript(Graph $graph)
{
$directed = false;
foreach ($graph->getEdges() as $edge) {
if ($edge instanceof EdgeDirected) {
$directed = true;
break;
}
}
/*
* The website [http://www.graphviz.org/content/dot-language] uses the term `ID` when displaying
* the abstract grammar for the DOT language.
* But the man pages for dot use the term `name` when describing the graph file language.
*/
$name = $graph->getAttribute('graphviz.name');
if ($name !== null) {
$name = $this->escapeId($name) . ' ';
}
$script = ($directed ? 'di':'') . 'graph ' . $name . '{' . self::EOL;
// add global attributes
$globals = array(
'graph' => 'graphviz.graph.',
'node' => 'graphviz.node.',
'edge' => 'graphviz.edge.',
);
foreach ($globals as $key => $prefix) {
$bag = new AttributeBagNamespaced($graph, $prefix);
if ($layout = $bag->getAttributes()) {
$script .= $this->formatIndent . $key . ' ' . $this->escapeAttributes($layout) . self::EOL;
}
}
$groups = array();
foreach ($graph->getVertices()->getMap() as $vid => $vertex) {
$groups[$vertex->getGroup()][$vid] = $vertex;
}
// only cluster vertices into groups if there are at least 2 different groups
if (count($groups) > 1) {
$indent = str_repeat($this->formatIndent, 2);
// put each group of vertices in a separate subgraph cluster
foreach ($groups as $group => $vertices) {
$script .= $this->formatIndent . 'subgraph cluster_' . $group . ' {' . self::EOL .
$indent . 'label = ' . $this->escape($group) . self::EOL;
foreach ($vertices as $vid => $vertex) {
$layout = $this->getLayoutVertex($vertex);
$script .= $indent . $this->escapeId($vid);
if ($layout) {
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
}
$script .= ' }' . self::EOL;
}
} else {
// explicitly add all isolated vertices (vertices with no edges) and vertices with special layout set
// other vertices wil be added automatically due to below edge definitions
foreach ($graph->getVertices()->getMap() as $vid => $vertex){
$layout = $this->getLayoutVertex($vertex);
if ($layout || $vertex->getEdges()->isEmpty()) {
$script .= $this->formatIndent . $this->escapeId($vid);
if ($layout) {
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
}
}
}
$edgeop = $directed ? ' -> ' : ' -- ';
// add all edges as directed edges
foreach ($graph->getEdges() as $currentEdge) {
$both = $currentEdge->getVertices()->getVector();
$currentStartVertex = $both[0];
$currentTargetVertex = $both[1];
$script .= $this->formatIndent . $this->escapeId($currentStartVertex->getId()) . $edgeop . $this->escapeId($currentTargetVertex->getId());
$layout = $this->getLayoutEdge($currentEdge);
// this edge is not a loop and also points to the opposite direction => this is actually an undirected edge
if ($directed && $currentStartVertex !== $currentTargetVertex && $currentEdge->isConnection($currentTargetVertex, $currentStartVertex)) {
$layout['dir'] = 'none';
}
if ($layout) {
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
}
$script .= '}' . self::EOL;
return $script;
}
/**
* escape given id string and wrap in quotes if needed
*
* @param string $id
* @return string
* @link http://graphviz.org/content/dot-language
*/
private function escapeId($id)
{
return self::escape($id);
}
public static function escape($id)
{
// see raw()
if ($id instanceof \stdClass && isset($id->string)) {
return $id->string;
}
// see @link: There is no semantic difference between abc_2 and "abc_2"
// numeric or simple string, no need to quote (only for simplicity)
if (preg_match('/^(?:\-?(?:\.\d+|\d+(?:\.\d+)?))$/i', $id)) {
return $id;
}
return '"' . str_replace(array('&', '<', '>', '"', "'", '\\', "\n"), array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '\\\\', '\\l'), $id) . '"';
}
/**
* get escaped attribute string for given array of (unescaped) attributes
*
* @param array $attrs
* @return string
* @uses GraphViz::escapeId()
*/
private function escapeAttributes($attrs)
{
$script = '[';
$first = true;
foreach ($attrs as $name => $value) {
if ($first) {
$first = false;
} else {
$script .= ' ';
}
$script .= $name . '=' . self::escape($value);
}
$script .= ']';
return $script;
}
/**
* create a raw string representation, i.e. do NOT escape the given string when used in graphviz output
*
* @param string $string
* @return \stdClass
* @see GraphViz::escape()
*/
public static function raw($string)
{
return (object) array('string' => $string);
}
protected function getLayoutVertex(Vertex $vertex)
{
$bag = new AttributeBagNamespaced($vertex, 'graphviz.');
$layout = $bag->getAttributes();
$balance = $vertex->getBalance();
if($balance !== NULL){
if($balance > 0){
$balance = '+' . $balance;
}
if(!isset($layout['label'])){
$layout['label'] = $vertex->getId();
}
$layout['label'] .= ' (' . $balance . ')';
}
return $layout;
}
protected function getLayoutEdge(Edge $edge)
{
$bag = new AttributeBagNamespaced($edge, 'graphviz.');
$layout = $bag->getAttributes();
// use flow/capacity/weight as edge label
$label = NULL;
$flow = $edge->getFlow();
$capacity = $edge->getCapacity();
// flow is set
if ($flow !== NULL) {
// NULL capacity = infinite capacity
$label = $flow . '/' . ($capacity === NULL ? '∞' : $capacity);
// capacity set, but not flow (assume zero flow)
} elseif ($capacity !== NULL) {
$label = '0/' . $capacity;
}
$weight = $edge->getWeight();
// weight is set
if ($weight !== NULL) {
if ($label === NULL) {
$label = $weight;
} else {
$label .= '/' . $weight;
}
}
if ($label !== NULL) {
if (isset($layout['label'])) {
$layout['label'] .= ' ' . $label;
} else {
$layout['label'] = $label;
}
}
return $layout;
}
}

40
vendor/graphp/graphviz/src/Image.php vendored Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace Graphp\GraphViz;
use Graphp\GraphViz\GraphViz;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Exporter\ExporterInterface;
class Image implements ExporterInterface
{
private $graphviz;
public function __construct(GraphViz $graphviz = null)
{
if ($graphviz === null) {
$graphviz = new GraphViz();
$graphviz->setFormat('png');
}
$this->graphviz = $graphviz;
}
public function getOutput(Graph $graph)
{
return $this->graphviz->createImageData($graph);
}
/**
* set the image output format to use
*
* @param string $type png, svg
* @return self $this (chainable)
* @uses GraphViz::setFormat()
*/
public function setFormat($type)
{
$this->graphviz->setFormat($type);
return $this;
}
}

View File

@@ -0,0 +1,386 @@
<?php
use Fhaculty\Graph\Graph;
use Graphp\GraphViz\GraphViz;
use PHPUnit\Framework\TestCase;
class GraphVizTest extends TestCase
{
private $graphViz;
public function setUp()
{
$this->graphViz = new GraphViz();
}
public function testGraphEmpty()
{
$graph = new Graph();
$expected = <<<VIZ
graph {
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphWithName()
{
$graph = new Graph();
$graph->setAttribute('graphviz.name', 'G');
$expected = <<<VIZ
graph "G" {
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphWithNameWithSpaces()
{
$graph = new Graph();
$graph->setAttribute('graphviz.name', 'My Graph Name');
$expected = <<<VIZ
graph "My Graph Name" {
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphIsolatedVertices()
{
$graph = new Graph();
$graph->createVertex('a');
$graph->createVertex('b');
$expected = <<<VIZ
graph {
"a"
"b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphIsolatedVerticesWithGroupsWillBeAddedToClusters()
{
$graph = new Graph();
$graph->createVertex('a')->setGroup(0);
$graph->createVertex('b')->setGroup(1)->setAttribute('graphviz.label', 'second');
$expected = <<<VIZ
graph {
subgraph cluster_0 {
label = 0
"a"
}
subgraph cluster_1 {
label = 1
"b" [label="second"]
}
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphDefaultAttributes()
{
$graph = new Graph();
$graph->setAttribute('graphviz.graph.bgcolor', 'transparent');
$graph->setAttribute('graphviz.node.color', 'blue');
$graph->setAttribute('graphviz.edge.color', 'grey');
$expected = <<<VIZ
graph {
graph [bgcolor="transparent"]
node [color="blue"]
edge [color="grey"]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testUnknownGraphAttributesWillBeDiscarded()
{
$graph = new Graph();
$graph->setAttribute('graphviz.vertex.color', 'blue');
$graph->setAttribute('graphviz.unknown.color', 'red');
$expected = <<<VIZ
graph {
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testEscaping()
{
$graph = new Graph();
$graph->createVertex('a');
$graph->createVertex('b¹²³ is; ok\\ay, "right"?');
$graph->createVertex(3);
$graph->createVertex(4)->setAttribute('graphviz.label', 'normal');
$graph->createVertex(5)->setAttribute('graphviz.label', GraphViz::raw('<raw>'));
$expected = <<<VIZ
graph {
"a"
"b¹²³ is; ok\\\\ay, &quot;right&quot;?"
3
4 [label="normal"]
5 [label=<raw>]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphWithSimpleEdgeUsesGraphWithSimpleEdgeDefinition()
{
// a -- b
$graph = new Graph();
$graph->createVertex('a')->createEdge($graph->createVertex('b'));
$expected = <<<VIZ
graph {
"a" -- "b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphWithLoopUsesGraphWithSimpleLoopDefinition()
{
// a -- b -\
// | |
// \--/
$graph = new Graph();
$graph->createVertex('a')->createEdge($graph->createVertex('b'));
$graph->getVertex('b')->createEdge($graph->getVertex('b'));
$expected = <<<VIZ
graph {
"a" -- "b"
"b" -- "b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphDirectedUsesDigraph()
{
$graph = new Graph();
$graph->createVertex('a')->createEdgeTo($graph->createVertex('b'));
$expected = <<<VIZ
digraph {
"a" -> "b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphDirectedWithLoopUsesDigraphWithSimpleLoopDefinition()
{
// a -> b -\
// ^ |
// \--/
$graph = new Graph();
$graph->createVertex('a')->createEdgeTo($graph->createVertex('b'));
$graph->getVertex('b')->createEdgeTo($graph->getVertex('b'));
$expected = <<<VIZ
digraph {
"a" -> "b"
"b" -> "b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphMixedUsesDigraphWithExplicitDirectionNoneForUndirectedEdges()
{
// a -> b -- c
$graph = new Graph();
$graph->createVertex('a')->createEdgeTo($graph->createVertex('b'));
$graph->createVertex('c')->createEdge($graph->getVertex('b'));
$expected = <<<VIZ
digraph {
"a" -> "b"
"c" -> "b" [dir="none"]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphMixedWithDirectedLoopUsesDigraphWithoutDirectionForDirectedLoop()
{
// a -- b -\
// ^ |
// \--/
$graph = new Graph();
$graph->createVertex('a')->createEdge($graph->createVertex('b'));
$graph->getVertex('b')->createEdgeTo($graph->getVertex('b'));
$expected = <<<VIZ
digraph {
"a" -> "b" [dir="none"]
"b" -> "b"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testGraphUndirectedWithIsolatedVerticesFirst()
{
// a -- b -- c d
$graph = new Graph();
$graph->createVertices(array('a', 'b', 'c', 'd'));
$graph->getVertex('a')->createEdge($graph->getVertex('b'));
$graph->getVertex('b')->createEdge($graph->getVertex('c'));
$expected = <<<VIZ
graph {
"d"
"a" -- "b"
"b" -- "c"
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testVertexLabels()
{
$graph = new Graph();
$graph->createVertex('a')->setBalance(1);
$graph->createVertex('b')->setBalance(0);
$graph->createVertex('c')->setBalance(-1);
$graph->createVertex('d')->setAttribute('graphviz.label', 'test');
$graph->createVertex('e')->setBalance(2)->setAttribute('graphviz.label', 'unnamed');
$expected = <<<VIZ
graph {
"a" [label="a (+1)"]
"b" [label="b (0)"]
"c" [label="c (-1)"]
"d" [label="test"]
"e" [label="unnamed (+2)"]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testEdgeLayoutAtributes()
{
$graph = new Graph();
$graph->createVertex('1a')->createEdge($graph->createVertex('1b'));
$graph->createVertex('2a')->createEdge($graph->createVertex('2b'))->setAttribute('graphviz.numeric', 20);
$graph->createVertex('3a')->createEdge($graph->createVertex('3b'))->setAttribute('graphviz.textual', "forty");
$graph->createVertex('4a')->createEdge($graph->createVertex('4b'))->getAttributeBag()->setAttributes(array('graphviz.1' => 1, 'graphviz.2' => 2));
$graph->createVertex('5a')->createEdge($graph->createVertex('5b'))->getAttributeBag()->setAttributes(array('graphviz.a' => 'b', 'graphviz.c' => 'd'));
$expected = <<<VIZ
graph {
"1a" -- "1b"
"2a" -- "2b" [numeric=20]
"3a" -- "3b" [textual="forty"]
"4a" -- "4b" [1=1 2=2]
"5a" -- "5b" [a="b" c="d"]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testEdgeLabels()
{
$graph = new Graph();
$graph->createVertex('1a')->createEdge($graph->createVertex('1b'));
$graph->createVertex('2a')->createEdge($graph->createVertex('2b'))->setWeight(20);
$graph->createVertex('3a')->createEdge($graph->createVertex('3b'))->setCapacity(30);
$graph->createVertex('4a')->createEdge($graph->createVertex('4b'))->setFlow(40);
$graph->createVertex('5a')->createEdge($graph->createVertex('5b'))->setFlow(50)->setCapacity(60);
$graph->createVertex('6a')->createEdge($graph->createVertex('6b'))->setFlow(60)->setCapacity(70)->setWeight(80);
$graph->createVertex('7a')->createEdge($graph->createVertex('7b'))->setFlow(70)->setAttribute('graphviz.label', 'prefixed');
$expected = <<<VIZ
graph {
"1a" -- "1b"
"2a" -- "2b" [label=20]
"3a" -- "3b" [label="0/30"]
"4a" -- "4b" [label="40/∞"]
"5a" -- "5b" [label="50/60"]
"6a" -- "6b" [label="60/70/80"]
"7a" -- "7b" [label="prefixed 70/∞"]
}
VIZ;
$this->assertEquals($expected, $this->graphViz->createScript($graph));
}
public function testCreateImageSrcWillExportPngDefaultFormat()
{
$graph = new Graph();
$src = $this->graphViz->createImageSrc($graph);
$this->assertStringStartsWith('data:image/png;base64,', $src);
}
public function testCreateImageSrcAsSvgWithUtf8DefaultCharset()
{
$graph = new Graph();
$this->graphViz->setFormat('svg');
$src = $this->graphViz->createImageSrc($graph);
$this->assertStringStartsWith('data:image/svg+xml;charset=UTF-8;base64,', $src);
}
public function testCreateImageSrcAsSvgzWithExplicitIsoCharsetLatin1()
{
$graph = new Graph();
$graph->setAttribute('graphviz.graph.charset', 'iso-8859-1');
$this->graphViz->setFormat('svgz');
$src = $this->graphViz->createImageSrc($graph);
$this->assertStringStartsWith('data:image/svg+xml;charset=iso-8859-1;base64,', $src);
}
}