├── .github ├── dependabot.yml └── workflows │ └── push.yml ├── .gitignore ├── .scrutinizer.yml ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── extension.neon ├── phpcs.xml.dist ├── phpmd.xml.dist ├── phpstan.neon ├── phpunit.xml.dist ├── psalm.xml ├── src └── phpDocumentor │ ├── GraphViz │ ├── Attribute.php │ ├── AttributeNotFound.php │ ├── Attributes.php │ ├── Edge.php │ ├── Exception.php │ ├── Graph.php │ └── Node.php │ └── PHPStan │ ├── AttributeGetterMethodReflection.php │ ├── AttributeSetterMethodReflection.php │ ├── GraphNodeReflectionExtension.php │ ├── MethodReflectionExtension.php │ └── assets │ └── attributes.xml └── tests └── phpDocumentor ├── GraphViz └── Test │ ├── AttributeTest.php │ ├── EdgeTest.php │ ├── GraphTest.php │ └── NodeTest.php └── PHPStan └── MethodReflectionExtensionTest.php /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | name: Qa workflow 7 | env: 8 | extensions: mbstring, intl, iconv, libxml, dom, json, simplexml, zlib, fileinfo 9 | key: cache-v1 # can be any string, change to clear the extension cache. 10 | defaultPHPVersion: '7.3' 11 | jobs: 12 | phpunit-with-coverage: 13 | runs-on: ubuntu-latest 14 | name: Unit tests 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Install graphviz 19 | run: sudo apt-get update && sudo apt-get install -y graphviz 20 | 21 | - name: Setup PHP 22 | uses: shivammathur/setup-php@v2 23 | with: 24 | php-version: ${{ env.defaultPHPVersion }} 25 | ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 26 | tools: phive 27 | 28 | - name: Get composer cache directory 29 | id: composer-cache 30 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 31 | 32 | - name: Cache composer dependencies 33 | uses: actions/cache@v2 34 | with: 35 | path: ${{ steps.composer-cache.outputs.dir }} 36 | key: composer-${{ hashFiles('**/composer.lock') }} 37 | restore-keys: composer- 38 | 39 | - name: Install Composer dependencies 40 | run: | 41 | composer install --no-progress --prefer-dist --optimize-autoloader 42 | 43 | - name: Run PHPUnit 44 | run: php vendor/bin/phpunit 45 | 46 | phpunit: 47 | runs-on: ${{ matrix.operating-system }} 48 | strategy: 49 | matrix: 50 | operating-system: 51 | - ubuntu-latest 52 | php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] 53 | name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }} 54 | needs: 55 | - phpunit-with-coverage 56 | steps: 57 | - uses: actions/checkout@v2 58 | 59 | - name: Install graphviz 60 | run: sudo apt-get update && sudo apt-get install -y graphviz 61 | 62 | - name: Setup PHP 63 | uses: shivammathur/setup-php@v2 64 | with: 65 | php-version: ${{ matrix.php-versions }} 66 | extension-csv: mbstring, simplexml 67 | ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 68 | 69 | - name: Get composer cache directory 70 | id: composer-cache 71 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 72 | 73 | - name: Cache composer dependencies 74 | uses: actions/cache@v2 75 | with: 76 | path: ${{ steps.composer-cache.outputs.dir }} 77 | key: composer-${{ hashFiles('**/composer.lock') }} 78 | restore-keys: composer- 79 | 80 | - name: Install Composer dependencies 81 | run: | 82 | composer install --no-progress --prefer-dist --optimize-autoloader 83 | 84 | - name: Run PHPUnit 85 | run: php vendor/bin/phpunit 86 | 87 | codestyle: 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v2 91 | - name: Restore/cache vendor folder 92 | uses: actions/cache@v1 93 | with: 94 | path: vendor 95 | key: all-build-${{ hashFiles('**/composer.lock') }} 96 | restore-keys: | 97 | all-build-${{ hashFiles('**/composer.lock') }} 98 | all-build- 99 | - name: Restore/cache tools folder 100 | uses: actions/cache@v1 101 | with: 102 | path: tools 103 | key: all-tools-${{ github.sha }} 104 | restore-keys: | 105 | all-tools-${{ github.sha }}- 106 | all-tools- 107 | - name: Code style check 108 | uses: docker://phpdoc/phpcs-ga:latest 109 | env: 110 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 111 | with: 112 | args: -d memory_limit=1024M 113 | 114 | phpstan: 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v2 118 | - name: Setup PHP 119 | uses: shivammathur/setup-php@v2 120 | with: 121 | php-version: ${{ env.defaultPHPVersion }} 122 | ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 123 | tools: pecl 124 | 125 | - name: Get composer cache directory 126 | id: composer-cache 127 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 128 | 129 | - name: Cache composer dependencies 130 | uses: actions/cache@v2 131 | with: 132 | path: ${{ steps.composer-cache.outputs.dir }} 133 | key: composer-${{ hashFiles('**/composer.lock') }} 134 | restore-keys: composer- 135 | 136 | - name: Install Composer dependencies 137 | run: | 138 | composer install --no-progress --prefer-dist --optimize-autoloader 139 | 140 | - name: PHPStan 141 | uses: phpDocumentor/phpstan-ga@master 142 | env: 143 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 144 | with: 145 | args: analyse src tests --configuration phpstan.neon 146 | 147 | psalm: 148 | runs-on: ubuntu-latest 149 | steps: 150 | - uses: actions/checkout@v2 151 | 152 | - name: Setup PHP 153 | uses: shivammathur/setup-php@v2 154 | with: 155 | php-version: 7.3 156 | ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 157 | tools: pecl, psalm 158 | 159 | - name: Get composer cache directory 160 | id: composer-cache 161 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 162 | 163 | - name: Cache composer dependencies 164 | uses: actions/cache@v2 165 | with: 166 | path: ${{ steps.composer-cache.outputs.dir }} 167 | key: composer-${{ hashFiles('**/composer.lock') }} 168 | restore-keys: composer- 169 | 170 | - name: Install Composer dependencies 171 | run: | 172 | composer install --no-progress --prefer-dist --optimize-autoloader 173 | 174 | - name: Psalm 175 | run: vendor/bin/psalm.phar --output-format=github 176 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make 2 | # it easy to contribute 3 | .idea 4 | /nbproject/private/ 5 | .buildpath 6 | .project 7 | .settings 8 | 9 | # Build folder and vendor folder are generated code; no need to version this 10 | build/ 11 | vendor/ 12 | temp/ 13 | tools/ 14 | *.phar 15 | 16 | # By default the phpunit.xml.dist is provided; you can override this using a local config file 17 | phpunit.xml 18 | composer.lock 19 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | before_commands: 2 | - "composer install --no-dev --prefer-source" 3 | 4 | checks: 5 | php: 6 | excluded_dependencies: 7 | - phpstan/phpstan 8 | 9 | tools: 10 | external_code_coverage: 11 | enabled: true 12 | timeout: 300 13 | filter: 14 | excluded_paths: ["tests", "vendor"] 15 | php_code_sniffer: 16 | enabled: true 17 | config: 18 | standard: PSR2 19 | filter: 20 | paths: ["src/*", "tests/*"] 21 | excluded_paths: [] 22 | php_cpd: 23 | enabled: true 24 | excluded_dirs: ["tests", "vendor"] 25 | php_cs_fixer: 26 | enabled: true 27 | config: 28 | level: all 29 | filter: 30 | paths: ["src/*", "tests/*"] 31 | php_loc: 32 | enabled: true 33 | excluded_dirs: ["tests", "vendor"] 34 | php_mess_detector: 35 | enabled: true 36 | config: 37 | ruleset: phpmd.xml.dist 38 | design_rules: { eval_expression: false } 39 | filter: 40 | paths: ["src/*"] 41 | php_pdepend: 42 | enabled: true 43 | excluded_dirs: ["tests", "vendor"] 44 | php_analyzer: 45 | enabled: true 46 | filter: 47 | paths: ["src/*", "tests/*"] 48 | sensiolabs_security_checker: true 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mike van Riel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install-phive 2 | install-phive: 3 | mkdir tools; \ 4 | wget -O tools/phive.phar https://phar.io/releases/phive.phar; \ 5 | wget -O tools/phive.phar.asc https://phar.io/releases/phive.phar.asc; \ 6 | gpg --keyserver pool.sks-keyservers.net --recv-keys 0x9D8A98B29B2D5D79; \ 7 | gpg --verify tools/phive.phar.asc tools/phive.phar; \ 8 | chmod +x tools/phive.phar 9 | 10 | .PHONY: setup 11 | setup: install-phive 12 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phar-ga:latest php tools/phive.phar install --force-accept-unsigned 13 | .PHONY: phpcbf 14 | phpcbf: 15 | docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest phpcbf ${ARGS} 16 | 17 | .PHONY: phpcs 18 | phpcs: 19 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest -d memory_limit=1024M -s 20 | 21 | .PHONY: phpstan 22 | phpstan: 23 | docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project phpdoc/phpstan-ga:latest analyse src tests --configuration phpstan.neon ${ARGS} 24 | 25 | .PHONY: psalm 26 | psalm: 27 | docker run -it --rm -v${CURDIR}:/data -w /data php:7.3 vendor/bin/psalm.phar 28 | 29 | .PHONY: test 30 | test: 31 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.3 vendor/bin/phpunit 32 | 33 | .PHONY: pre-commit-test 34 | pre-commit-test: phpcs phpstan psalm test 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 2 | [![Travis Status](https://img.shields.io/travis/phpDocumentor/GraphViz.svg?label=Linux)](https://travis-ci.org/phpDocumentor/GraphViz) 3 | [![Appveyor Status](https://img.shields.io/appveyor/ci/phpDocumentor/GraphViz.svg?label=Windows)](https://ci.appveyor.com/project/phpDocumentor/GraphViz/branch/master) 4 | [![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/GraphViz.svg)](https://coveralls.io/github/phpDocumentor/GraphViz?branch=master) 5 | [![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/GraphViz.svg)](https://scrutinizer-ci.com/g/phpDocumentor/GraphViz/?branch=master) 6 | [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/GraphViz.svg)](https://scrutinizer-ci.com/g/phpDocumentor/GraphViz/?branch=master) 7 | [![Stable Version](https://img.shields.io/packagist/v/phpDocumentor/GraphViz.svg)](https://packagist.org/packages/phpDocumentor/GraphViz) 8 | [![Unstable Version](https://img.shields.io/packagist/vpre/phpDocumentor/GraphViz.svg)](https://packagist.org/packages/phpDocumentor/GraphViz) 9 | 10 | 11 | GraphViz 12 | ======== 13 | 14 | GraphViz is a library meant for generating .dot files for GraphViz with a 15 | fluent interface. 16 | 17 | 18 | ### PHPStan extension 19 | 20 | This library contains a number of magic methods to set attributes on `Node`, `Graph` and `Edge` 21 | this will result in errors when using the library with checks by PHPStan. For your convenience this 22 | library provides an phpStan extension so your code can be checked correctly by phpstan. 23 | 24 | ``` 25 | includes: 26 | - vendor/phpdocumentor/graphviz/extension.neon 27 | ``` 28 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpdocumentor/graphviz", 3 | "description": "Wrapper for Graphviz", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Mike van Riel", 9 | "email": "mike.vanriel@naenius.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^7.2 || ^8.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "phpDocumentor\\GraphViz\\": "src/phpDocumentor/GraphViz", 18 | "phpDocumentor\\GraphViz\\PHPStan\\": "./src/phpDocumentor/PHPStan" 19 | } 20 | }, 21 | "autoload-dev": { 22 | "psr-4": { 23 | "phpDocumentor\\GraphViz\\Test\\": "./tests/phpDocumentor/GraphViz/Test", 24 | "phpDocumentor\\GraphViz\\PHPStan\\": "./tests/phpDocumentor/PHPStan" 25 | } 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^8.2 || ^9.2", 29 | "mockery/mockery": "^1.2", 30 | "phpstan/phpstan": "^0.12", 31 | "ext-simplexml": "*", 32 | "psalm/phar": "^4.15 || ^5.0" 33 | }, 34 | "extra": { 35 | "branch-alias": { 36 | "dev-master": "2.x-dev" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /extension.neon: -------------------------------------------------------------------------------- 1 | services: 2 | - 3 | class: phpDocumentor\GraphViz\PHPStan\MethodReflectionExtension 4 | tags: 5 | - phpstan.broker.methodsClassReflectionExtension 6 | - 7 | class: phpDocumentor\GraphViz\PHPStan\GraphNodeReflectionExtension 8 | tags: 9 | - phpstan.broker.propertiesClassReflectionExtension 10 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | The coding standard for phpDocumentor. 4 | 5 | src 6 | tests 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | tests/phpDocumentor/GraphViz/Test/GraphTest\.php 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /phpmd.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 40 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - /composer/vendor/phpstan/phpstan-mockery/extension.neon 3 | - ./extension.neon 4 | 5 | parameters: 6 | level: max 7 | ignoreErrors: 8 | #We have some runtime protection that needs to be tested. Ignore these errors 9 | - '#Call to an undefined method phpDocumentor\\GraphViz\\Edge::someNonExcistingMethod\(\)\.#' 10 | - '#Call to an undefined method phpDocumentor\\GraphViz\\Graph::MyMethod\(\)\.#' 11 | - '#Call to an undefined method phpDocumentor\\GraphViz\\Graph::getNotExisting\(\)\.#' 12 | - '#Call to an undefined method phpDocumentor\\GraphViz\\Node::someNonExistingMethod\(\)\.#' 13 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | ./tests/phpDocumentor/GraphViz 18 | 19 | 20 | ./tests/phpDocumentor/PHPStan 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ./src/ 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/phpDocumentor/GraphViz/Attribute.php: -------------------------------------------------------------------------------- 1 | key = $key; 42 | $this->value = $value; 43 | } 44 | 45 | /** 46 | * Sets the key for this attribute. 47 | * 48 | * @param string $key The new name of this attribute. 49 | */ 50 | public function setKey(string $key): self 51 | { 52 | $this->key = $key; 53 | 54 | return $this; 55 | } 56 | 57 | /** 58 | * Returns the name for this attribute. 59 | */ 60 | public function getKey(): string 61 | { 62 | return $this->key; 63 | } 64 | 65 | /** 66 | * Sets the value for this attribute. 67 | * 68 | * @param string $value The new value. 69 | */ 70 | public function setValue(string $value): self 71 | { 72 | $this->value = $value; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * Returns the value for this attribute. 79 | */ 80 | public function getValue(): string 81 | { 82 | return $this->value; 83 | } 84 | 85 | /** 86 | * Returns the attribute definition as is requested by GraphViz. 87 | */ 88 | public function __toString(): string 89 | { 90 | $key = $this->getKey(); 91 | if ($key === 'url') { 92 | $key = 'URL'; 93 | } 94 | 95 | $value = $this->getValue(); 96 | if ($this->isValueContainingSpecials()) { 97 | $value = '"' . $this->encodeSpecials() . '"'; 98 | } elseif (!$this->isValueInHtml()) { 99 | $value = '"' . addslashes($value) . '"'; 100 | } 101 | 102 | return $key . '=' . $value; 103 | } 104 | 105 | /** 106 | * Returns whether the value contains HTML. 107 | */ 108 | public function isValueInHtml(): bool 109 | { 110 | $value = $this->getValue(); 111 | 112 | return isset($value[0]) && ($value[0] === '<'); 113 | } 114 | 115 | /** 116 | * Checks whether the value contains any any special characters needing escaping. 117 | */ 118 | public function isValueContainingSpecials(): bool 119 | { 120 | return strstr($this->getValue(), '\\') !== false; 121 | } 122 | 123 | /** 124 | * Encode special characters so the escape sequences aren't removed 125 | * 126 | * @see http://www.graphviz.org/doc/info/attrs.html#k:escString 127 | */ 128 | protected function encodeSpecials(): string 129 | { 130 | $value = $this->getValue(); 131 | $regex = '(\'|"|\\x00|\\\\(?![\\\\NGETHLnlr]))'; 132 | 133 | return (string) preg_replace($regex, '\\\\$0', $value); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/phpDocumentor/GraphViz/AttributeNotFound.php: -------------------------------------------------------------------------------- 1 | attributes[$name] = new Attribute($name, $value); 26 | 27 | return $this; 28 | } 29 | 30 | /** 31 | * @throws AttributeNotFound 32 | */ 33 | public function getAttribute(string $name): Attribute 34 | { 35 | if (!array_key_exists($name, $this->attributes)) { 36 | throw new AttributeNotFound($name); 37 | } 38 | 39 | return $this->attributes[$name]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/phpDocumentor/GraphViz/Edge.php: -------------------------------------------------------------------------------- 1 | from = $from; 46 | $this->to = $to; 47 | } 48 | 49 | /** 50 | * Factory method used to assist with fluent interface handling. 51 | * 52 | * See the examples for more details. 53 | * 54 | * @param Node $from Starting node to create an Edge from. 55 | * @param Node $to Destination node where to create and 56 | * edge to. 57 | */ 58 | public static function create(Node $from, Node $to): self 59 | { 60 | return new self($from, $to); 61 | } 62 | 63 | /** 64 | * Returns the source Node for this Edge. 65 | */ 66 | public function getFrom(): Node 67 | { 68 | return $this->from; 69 | } 70 | 71 | /** 72 | * Returns the destination Node for this Edge. 73 | */ 74 | public function getTo(): Node 75 | { 76 | return $this->to; 77 | } 78 | 79 | /** 80 | * Magic method to provide a getter/setter to add attributes on the edge. 81 | * 82 | * Using this method we make sure that we support any attribute without too 83 | * much hassle. If the name for this method does not start with get or set 84 | * we return null. 85 | * 86 | * Set methods return this graph (fluent interface) whilst get methods 87 | * return the attribute value. 88 | * 89 | * @param string $name name of the invoked method, expect it to be 90 | * setX or getX. 91 | * @param mixed[] $arguments Arguments for the setter, only 1 is expected: value 92 | * 93 | * @return Attribute|Edge|null 94 | * 95 | * @throws AttributeNotFound 96 | */ 97 | public function __call(string $name, array $arguments) 98 | { 99 | $key = strtolower(substr($name, 3)); 100 | if (strtolower(substr($name, 0, 3)) === 'set') { 101 | return $this->setAttribute($key, (string) $arguments[0]); 102 | } 103 | 104 | if (strtolower(substr($name, 0, 3)) === 'get') { 105 | return $this->getAttribute($key); 106 | } 107 | 108 | return null; 109 | } 110 | 111 | /** 112 | * Returns the edge definition as is requested by GraphViz. 113 | */ 114 | public function __toString(): string 115 | { 116 | $attributes = []; 117 | foreach ($this->attributes as $value) { 118 | $attributes[] = (string) $value; 119 | } 120 | 121 | $attributes = implode("\n", $attributes); 122 | 123 | $fromName = addslashes($this->getFrom()->getName()); 124 | $toName = addslashes($this->getTo()->getName()); 125 | 126 | return << "${toName}" [ 128 | ${attributes} 129 | ] 130 | DOT; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/phpDocumentor/GraphViz/Exception.php: -------------------------------------------------------------------------------- 1 | setName($name) 88 | ->setType($directional ? 'digraph' : 'graph'); 89 | 90 | return $graph; 91 | } 92 | 93 | /** 94 | * Sets the path for the execution. Only needed if it is not in the PATH env. 95 | * 96 | * @param string $path The path to execute dot from 97 | */ 98 | public function setPath(string $path): self 99 | { 100 | $realpath = realpath($path); 101 | if ($path && $path === $realpath) { 102 | $this->path = $path . DIRECTORY_SEPARATOR; 103 | } 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Sets the name for this graph. 110 | * 111 | * If this is a subgraph you can prefix the name with _cluster_ to group all 112 | * contained nodes and add a border. 113 | * 114 | * @param string $name The new name for this graph. 115 | */ 116 | public function setName(string $name): self 117 | { 118 | $this->name = $name; 119 | 120 | return $this; 121 | } 122 | 123 | /** 124 | * Returns the name for this Graph. 125 | */ 126 | public function getName(): string 127 | { 128 | return $this->name; 129 | } 130 | 131 | /** 132 | * Sets the type for this graph. 133 | * 134 | * @param string $type Must be either "digraph", "graph" or "subgraph". 135 | * 136 | * @throws InvalidArgumentException If $type is not "digraph", "graph" or 137 | * "subgraph". 138 | */ 139 | public function setType(string $type): self 140 | { 141 | if (!in_array($type, ['digraph', 'graph', 'subgraph'], true)) { 142 | throw new InvalidArgumentException( 143 | 'The type for a graph must be either "digraph", "graph" or ' 144 | . '"subgraph"' 145 | ); 146 | } 147 | 148 | $this->type = $type; 149 | 150 | return $this; 151 | } 152 | 153 | /** 154 | * Returns the type of this Graph. 155 | */ 156 | public function getType(): string 157 | { 158 | return $this->type; 159 | } 160 | 161 | /** 162 | * Set if the Graph should be strict. If the graph is strict then 163 | * multiple edges are not allowed between the same pairs of nodes 164 | */ 165 | public function setStrict(bool $isStrict): self 166 | { 167 | $this->strict = $isStrict; 168 | 169 | return $this; 170 | } 171 | 172 | public function isStrict(): bool 173 | { 174 | return $this->strict; 175 | } 176 | 177 | /** 178 | * Magic method to provide a getter/setter to add attributes on the Graph. 179 | * 180 | * Using this method we make sure that we support any attribute without 181 | * too much hassle. If the name for this method does not start with get 182 | * or set we return null. 183 | * 184 | * Set methods return this graph (fluent interface) whilst get methods 185 | * return the attribute value. 186 | * 187 | * @param string $name Name of the method including get/set 188 | * @param mixed[] $arguments The arguments, should be 1: the value 189 | * 190 | * @return Attribute|Graph|null 191 | * 192 | * @throws AttributeNotFound 193 | */ 194 | public function __call(string $name, array $arguments) 195 | { 196 | $key = strtolower(substr($name, 3)); 197 | if (strtolower(substr($name, 0, 3)) === 'set') { 198 | return $this->setAttribute($key, (string) $arguments[0]); 199 | } 200 | 201 | if (strtolower(substr($name, 0, 3)) === 'get') { 202 | return $this->getAttribute($key); 203 | } 204 | 205 | return null; 206 | } 207 | 208 | /** 209 | * Adds a subgraph to this graph; automatically changes the type to subgraph. 210 | * 211 | * Please note that an index is maintained using the name of the subgraph. 212 | * Thus if you have 2 subgraphs with the same name that the first will be 213 | * overwritten by the latter. 214 | * 215 | * @see Graph::create() 216 | * 217 | * @param Graph $graph The graph to add onto this graph as 218 | * subgraph. 219 | */ 220 | public function addGraph(self $graph): self 221 | { 222 | $graph->setType('subgraph'); 223 | $this->graphs[$graph->getName()] = $graph; 224 | 225 | return $this; 226 | } 227 | 228 | /** 229 | * Checks whether a graph with a certain name already exists. 230 | * 231 | * @param string $name Name of the graph to find. 232 | */ 233 | public function hasGraph(string $name): bool 234 | { 235 | return isset($this->graphs[$name]); 236 | } 237 | 238 | /** 239 | * Returns the subgraph with a given name. 240 | * 241 | * @param string $name Name of the requested graph. 242 | */ 243 | public function getGraph(string $name): self 244 | { 245 | return $this->graphs[$name]; 246 | } 247 | 248 | /** 249 | * Sets a node in the $nodes array; uses the name of the node as index. 250 | * 251 | * Nodes can be retrieved by retrieving the property with the same name. 252 | * Thus 'node1' can be retrieved by invoking: $graph->node1 253 | * 254 | * @see Node::create() 255 | * 256 | * @param Node $node The node to set onto this Graph. 257 | */ 258 | public function setNode(Node $node): self 259 | { 260 | $this->nodes[$node->getName()] = $node; 261 | 262 | return $this; 263 | } 264 | 265 | /** 266 | * Finds a node in this graph or any of its subgraphs. 267 | * 268 | * @param string $name Name of the node to find. 269 | */ 270 | public function findNode(string $name): ?Node 271 | { 272 | if (isset($this->nodes[$name])) { 273 | return $this->nodes[$name]; 274 | } 275 | 276 | foreach ($this->graphs as $graph) { 277 | $node = $graph->findNode($name); 278 | if ($node) { 279 | return $node; 280 | } 281 | } 282 | 283 | return null; 284 | } 285 | 286 | /** 287 | * Sets a node using a custom name. 288 | * 289 | * @see Graph::setNode() 290 | * 291 | * @param string $name Name of the node. 292 | * @param Node $value Node to set on the given name. 293 | */ 294 | public function __set(string $name, Node $value): void 295 | { 296 | $this->nodes[$name] = $value; 297 | } 298 | 299 | /** 300 | * Returns the requested node by its name. 301 | * 302 | * @see Graph::setNode() 303 | * 304 | * @param string $name The name of the node to retrieve. 305 | */ 306 | public function __get(string $name): ?Node 307 | { 308 | return $this->nodes[$name] ?? null; 309 | } 310 | 311 | /** 312 | * Links two nodes to eachother and registers the Edge onto this graph. 313 | * 314 | * @see Edge::create() 315 | * 316 | * @param Edge $edge The link between two classes. 317 | */ 318 | public function link(Edge $edge): self 319 | { 320 | $this->edges[] = $edge; 321 | 322 | return $this; 323 | } 324 | 325 | /** 326 | * Exports this graph to a generated image. 327 | * 328 | * This is the only method that actually requires GraphViz. 329 | * 330 | * @link http://www.graphviz.org/content/output-formats 331 | * @uses GraphViz/dot 332 | * 333 | * @param string $type The type to export to; see the link above for a 334 | * list of supported types. 335 | * @param string $filename The path to write to. 336 | * 337 | * @throws Exception If an error occurred in GraphViz. 338 | */ 339 | public function export(string $type, string $filename): self 340 | { 341 | $type = escapeshellarg($type); 342 | $filename = escapeshellarg($filename); 343 | 344 | // write the dot file to a temporary file 345 | $tmpfile = (string) tempnam(sys_get_temp_dir(), 'gvz'); 346 | file_put_contents($tmpfile, (string) $this); 347 | 348 | // escape the temp file for use as argument 349 | $tmpfileArg = escapeshellarg($tmpfile); 350 | 351 | // create the dot output 352 | $output = []; 353 | $code = 0; 354 | exec($this->path . "dot -T${type} -o${filename} < ${tmpfileArg} 2>&1", $output, $code); 355 | unlink($tmpfile); 356 | 357 | if ($code !== 0) { 358 | throw new Exception( 359 | 'An error occurred while creating the graph; GraphViz returned: ' 360 | . implode(PHP_EOL, $output) 361 | ); 362 | } 363 | 364 | return $this; 365 | } 366 | 367 | /** 368 | * Generates a DOT file for use with GraphViz. 369 | * 370 | * GraphViz is not used in this method; it is safe to call it even without 371 | * GraphViz installed. 372 | */ 373 | public function __toString(): string 374 | { 375 | $elements = array_merge( 376 | $this->graphs, 377 | $this->attributes, 378 | $this->edges, 379 | $this->nodes 380 | ); 381 | 382 | $attributes = []; 383 | foreach ($elements as $value) { 384 | $attributes[] = (string) $value; 385 | } 386 | 387 | $attributes = implode(PHP_EOL, $attributes); 388 | 389 | $strict = ($this->isStrict() ? 'strict ' : ''); 390 | 391 | return <<getType()} "{$this->getName()}" { 393 | ${attributes} 394 | } 395 | DOT; 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/phpDocumentor/GraphViz/Node.php: -------------------------------------------------------------------------------- 1 | setName($name); 44 | if ($label === null) { 45 | return; 46 | } 47 | 48 | $this->setLabel($label); 49 | } 50 | 51 | /** 52 | * Factory method used to assist with fluent interface handling. 53 | * 54 | * See the examples for more details. 55 | * 56 | * @param string $name Name of the new node. 57 | * @param string|null $label Optional label text. 58 | */ 59 | public static function create(string $name, ?string $label = null): self 60 | { 61 | return new self($name, $label); 62 | } 63 | 64 | /** 65 | * Sets the name for this node. 66 | * 67 | * Not to confuse with the label. 68 | * 69 | * @param string $name Name for this node. 70 | */ 71 | public function setName(string $name): self 72 | { 73 | $this->name = $name; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Returns the name for this node. 80 | */ 81 | public function getName(): string 82 | { 83 | return $this->name; 84 | } 85 | 86 | /** 87 | * Magic method to provide a getter/setter to add attributes on the Node. 88 | * 89 | * Using this method we make sure that we support any attribute without 90 | * too much hassle. If the name for this method does not start with get or 91 | * set we return null. 92 | * 93 | * Set methods return this graph (fluent interface) whilst get methods 94 | * return the attribute value. 95 | * 96 | * @param string $name Method name; either getX or setX is expected. 97 | * @param mixed[] $arguments List of arguments; only 1 is expected for setX. 98 | * 99 | * @return Attribute|Node|null 100 | * 101 | * @throws AttributeNotFound 102 | */ 103 | public function __call(string $name, array $arguments) 104 | { 105 | $key = strtolower(substr($name, 3)); 106 | if (strtolower(substr($name, 0, 3)) === 'set') { 107 | return $this->setAttribute($key, (string) $arguments[0]); 108 | } 109 | 110 | if (strtolower(substr($name, 0, 3)) === 'get') { 111 | return $this->getAttribute($key); 112 | } 113 | 114 | return null; 115 | } 116 | 117 | /** 118 | * Returns the node definition as is requested by GraphViz. 119 | */ 120 | public function __toString(): string 121 | { 122 | $attributes = []; 123 | foreach ($this->attributes as $value) { 124 | $attributes[] = (string) $value; 125 | } 126 | 127 | $attributes = implode("\n", $attributes); 128 | 129 | $name = addslashes($this->getName()); 130 | 131 | return <<classReflection = $classReflection; 39 | $this->name = $name; 40 | } 41 | 42 | public function getDeclaringClass(): ClassReflection 43 | { 44 | return $this->classReflection; 45 | } 46 | 47 | public function isStatic(): bool 48 | { 49 | return false; 50 | } 51 | 52 | public function isPrivate(): bool 53 | { 54 | return false; 55 | } 56 | 57 | public function isPublic(): bool 58 | { 59 | return true; 60 | } 61 | 62 | public function getName(): string 63 | { 64 | return $this->name; 65 | } 66 | 67 | public function getPrototype(): ClassMemberReflection 68 | { 69 | return $this; 70 | } 71 | 72 | /** 73 | * @return ParametersAcceptor[] 74 | */ 75 | public function getVariants(): array 76 | { 77 | return [ 78 | new FunctionVariant( 79 | TemplateTypeMap::createEmpty(), 80 | null, 81 | [], 82 | false, 83 | new ObjectType(Attribute::class) 84 | ), 85 | ]; 86 | } 87 | 88 | public function getDocComment(): ?string 89 | { 90 | return null; 91 | } 92 | 93 | public function isDeprecated(): TrinaryLogic 94 | { 95 | return TrinaryLogic::createNo(); 96 | } 97 | 98 | public function getDeprecatedDescription(): ?string 99 | { 100 | return null; 101 | } 102 | 103 | public function isFinal(): TrinaryLogic 104 | { 105 | return TrinaryLogic::createNo(); 106 | } 107 | 108 | public function isInternal(): TrinaryLogic 109 | { 110 | return TrinaryLogic::createNo(); 111 | } 112 | 113 | public function getThrowType(): ?Type 114 | { 115 | return new ObjectType(AttributeNotFound::class); 116 | } 117 | 118 | public function hasSideEffects(): TrinaryLogic 119 | { 120 | return TrinaryLogic::createMaybe(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/phpDocumentor/PHPStan/AttributeSetterMethodReflection.php: -------------------------------------------------------------------------------- 1 | classReflection = $classReflection; 41 | $this->name = $name; 42 | $this->attributeType = $attributeType; 43 | } 44 | 45 | public function getDeclaringClass(): ClassReflection 46 | { 47 | return $this->classReflection; 48 | } 49 | 50 | public function isStatic(): bool 51 | { 52 | return false; 53 | } 54 | 55 | public function isPrivate(): bool 56 | { 57 | return false; 58 | } 59 | 60 | public function isPublic(): bool 61 | { 62 | return true; 63 | } 64 | 65 | public function getName(): string 66 | { 67 | return $this->name; 68 | } 69 | 70 | public function getPrototype(): ClassMemberReflection 71 | { 72 | return $this; 73 | } 74 | 75 | /** 76 | * @return ParametersAcceptor[] 77 | */ 78 | public function getVariants(): array 79 | { 80 | return [ 81 | new FunctionVariant( 82 | TemplateTypeMap::createEmpty(), 83 | TemplateTypeMap::createEmpty(), 84 | [ 85 | new DummyParameter('value', $this->attributeType, false, null, false, null), 86 | ], 87 | false, 88 | new ObjectType($this->classReflection->getName()) 89 | ), 90 | ]; 91 | } 92 | 93 | public function getDocComment(): ?string 94 | { 95 | return null; 96 | } 97 | 98 | public function isDeprecated(): TrinaryLogic 99 | { 100 | return TrinaryLogic::createNo(); 101 | } 102 | 103 | public function getDeprecatedDescription(): ?string 104 | { 105 | return null; 106 | } 107 | 108 | public function isFinal(): TrinaryLogic 109 | { 110 | return TrinaryLogic::createNo(); 111 | } 112 | 113 | public function isInternal(): TrinaryLogic 114 | { 115 | return TrinaryLogic::createNo(); 116 | } 117 | 118 | public function getThrowType(): ?Type 119 | { 120 | return new ObjectType(AttributeNotFound::class); 121 | } 122 | 123 | public function hasSideEffects(): TrinaryLogic 124 | { 125 | return TrinaryLogic::createYes(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/phpDocumentor/PHPStan/GraphNodeReflectionExtension.php: -------------------------------------------------------------------------------- 1 | getName() === Graph::class; 29 | } 30 | 31 | public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection 32 | { 33 | return new AnnotationPropertyReflection( 34 | $classReflection, 35 | new ObjectType(Node::class), 36 | true, 37 | true 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/phpDocumentor/PHPStan/MethodReflectionExtension.php: -------------------------------------------------------------------------------- 1 | 'node', 45 | Graph::class => 'graph', 46 | Edge::class => 'edge', 47 | ]; 48 | 49 | public function hasMethod(ClassReflection $classReflection, string $methodName): bool 50 | { 51 | if (!array_key_exists($classReflection->getName(), self::SUPPORTED_CLASSES)) { 52 | return false; 53 | } 54 | 55 | $methods = $this->getMethodsFromSpec(self::SUPPORTED_CLASSES[$classReflection->getName()]); 56 | $expectedAttribute = $this->getAttributeFromMethodName($methodName); 57 | 58 | return in_array($expectedAttribute, $methods, true); 59 | } 60 | 61 | public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection 62 | { 63 | if (stripos($methodName, 'get') === 0) { 64 | return new AttributeGetterMethodReflection($classReflection, $methodName); 65 | } 66 | 67 | $attributeName = $this->getAttributeFromMethodName($methodName); 68 | 69 | return new AttributeSetterMethodReflection( 70 | $classReflection, 71 | $methodName, 72 | $this->getAttributeInputType($attributeName) 73 | ); 74 | } 75 | 76 | /** 77 | * @return string[] 78 | */ 79 | private function getMethodsFromSpec(string $className): array 80 | { 81 | $simpleXml = $this->getAttributesXmlDoc(); 82 | 83 | $elements = $simpleXml->xpath(sprintf("xsd:complexType[@name='%s']/xsd:attribute", $className)); 84 | 85 | if ($elements === false) { 86 | throw new InvalidArgumentException( 87 | sprintf('Class "%s" does not exist in Graphviz spec', $className) 88 | ); 89 | } 90 | 91 | return array_map( 92 | static function (SimpleXMLElement $attribute): string { 93 | return strtolower((string) $attribute['ref']); 94 | }, 95 | $elements 96 | ); 97 | } 98 | 99 | private function getAttributeInputType(string $ref): Type 100 | { 101 | $simpleXml = $this->getAttributesXmlDoc(); 102 | $attributes = $simpleXml->xpath(sprintf("xsd:attribute[@name='%s']", $ref)); 103 | 104 | if (empty($attributes)) { 105 | return new StringType(); 106 | } 107 | 108 | $type = $attributes[0]['type']; 109 | $type = str_replace('xsd:', '', (string) $type); 110 | switch ($type) { 111 | case 'boolean': 112 | return new BooleanType(); 113 | 114 | case 'decimal': 115 | return new FloatType(); 116 | 117 | case 'string': 118 | default: 119 | return new StringType(); 120 | } 121 | } 122 | 123 | private function getAttributesXmlDoc(): SimpleXMLElement 124 | { 125 | $fileContent = file_get_contents(__DIR__ . '/assets/attributes.xml'); 126 | 127 | if ($fileContent === false) { 128 | throw new RuntimeException('Cannot read attributes spec'); 129 | } 130 | 131 | $xml = simplexml_load_string($fileContent); 132 | if ($xml === false) { 133 | throw new RuntimeException('Cannot read attributes spec'); 134 | } 135 | 136 | return $xml; 137 | } 138 | 139 | private function getAttributeFromMethodName(string $methodName): string 140 | { 141 | return strtolower(substr($methodName, 3)); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/phpDocumentor/PHPStan/assets/attributes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | All Graphviz attributes are specified by name-value pairs. Thus, to 26 | set the fillcolor of a node abc, one would use 27 | 28 | 29 | abc [fillcolor = red] 30 | 31 | 32 | Similarly, to set the arrowhead style of an edge abc -> def, 33 | one would use 34 | 35 | 36 | abc -> def [arrowhead = diamond] 37 | 38 | 39 | Further details concerning the setting of attributes can be found 40 | in the description of the 41 | DOT language. 42 | 43 | 44 | 45 | 46 | 47 | 48 | At present, most device-independent units are either inches or 49 | points, 50 | which we take as 72 points per inch. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Some attributes, such as 60 | dir or arrowtail, are 61 | ambiguous when used in 62 | DOT 63 | with an undirected graph since the head and tail of an edge are meaningless. 64 | As a convention, the first time an undirected edge appears, the 65 | DOT 66 | parser will assign the left node as the tail node and the right node as 67 | the head. For example, the edge A -- B will have tail A 68 | and head B. It is the user's responsibility to handle such 69 | edges consistently. If the edge appears later, in the format 70 | 71 | 72 | B -- A [taillabel = "tail"] 73 | 74 | 75 | the drawing will attach the tail label to node A. 76 | To avoid possible confusion when such attributes are required, the user 77 | is encouraged to use a directed graph. 78 | If it is important to make the graph appear undirected, this can be 79 | done using the dir, arrowtail 80 | or arrowhead attributes. 81 | 82 | 83 | 84 | 85 | 86 | 87 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | For undirected edges T -- H;, one of the nodes, usually 114 | the righthand one, is treated as the head for the purpose of 115 | interpreting forward and back. 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | String allowing escape sequences which are replaced according 133 | to the context. 134 | For node attributes, the substring \N is replaced by the name of the node, 135 | and the substring \G by the name of the graph. 136 | For graph or cluster attributes, the substring \G is replaced by the 137 | name of the graph or cluster. 138 | For edge attributes, the substring \E is replaced by the name of the edge, 139 | the substring \G is replaced by the name of the graph or cluster, 140 | and the substrings \T and \H by the names of 141 | the tail and head nodes, respectively. 142 | The name of an edge is the string formed from the name of the 143 | tail node, the appropriate edge operator (-- or ->) and the name of the 144 | head node. 145 | 146 | 147 | In addition, if the associated attribute is 148 | label, 149 | headlabel or taillabel, 150 | the escape sequences \n, \l and \r 151 | divide the label into lines, centered, left-justified, and right-justified, 152 | respectively. 153 | 154 | 155 | 156 | 157 | 158 | 163 | 164 | 165 | 166 | 167 | 168 | These specify the order in which nodes and edges are drawn in concrete 169 | output. The default breadthfirst is the simplest, but when the graph 170 | layout does not avoid edge-node overlap, this mode will sometimes have 171 | edges drawn over nodes and sometimes on top of nodes. If the mode 172 | nodesfirst is chosen, all nodes are drawn first, followed by the 173 | edges. This guarantees an edge-node overlap will not be mistaken for 174 | an edge ending at a node. On the other hand, usually for aesthetic 175 | reasons, it may be desirable that all edges appear beneath nodes, even 176 | if the resulting drawing is ambiguous. This can be achieved by choosing 177 | edgesfirst. 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | These specify the granularity of packing connected components when 193 | the pack attribute is true. A value of node causes 194 | packing at the node and edge label, with no overlapping of these objects. 195 | This produces a layout with the least area, but it also allows interleaving, 196 | where a node of one component may lie between two nodes in another 197 | component. A value of graph does a packing using the bounding box of the 198 | component. Thus, there will be a rectangular region around a component 199 | free of elements of any other component. 200 | A value of clust guarantees that top-level clusters are kept intact. 201 | What effect a value has also depends on the layout algorithm. For 202 | example, neato does not support clusters, so a value of clust will 203 | have the same effect as the default node value. 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | These specify the 8 row or column major orders for traversing a 219 | rectangular array, the first character corresponding to the major 220 | order and the second to the minor order. Thus, for "BL", the 221 | major order is from bottom to top, and the minor order is from left 222 | to right. This means the bottom row is traversed first, from left 223 | to right, then the next row up, from left to right, and so on, 224 | until the topmost row is traversed. 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 244 | 245 | 246 | 247 | 248 | 249 | List of pointf, separated by spaces. 250 | 251 | 252 | 253 | 254 | 255 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | Corresponding to directed graphs drawn 274 | from top to bottom, from left to right, from bottom to top, and from 275 | right to left, respectively. 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | Specifies the path to images referenced within the graph. 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | Factor damping force motions. On each iteration, a nodes movement 352 | is limited to this factor of its potential motion. By being less than 353 | 1.0, the system tends to "cool", thereby preventing cycling. 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | Spring constant used in virtual physical model. It roughly corresponds 364 | to an ideal edge length (in inches), in that increasing K tends to 365 | increase the distance between nodes. 366 | Note that the edge attribute len can be used to 367 | override this value for adjacent nodes. 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | Hyperlinks incorporated into device-dependent output. 378 | At present, used in ps2, cmap, i*map and svg formats. 379 | For all these formats, URLs can be attached to nodes, edges and 380 | clusters. URL attributes can also be attached to the root graph in ps2, 381 | cmap and i*map formats. This serves as the base URL for relative URLs in the 382 | former, and as the default image map file in the latter. 383 | 384 | 385 | For svg, cmapx and imap output, the active area for a node is its 386 | visible image. 387 | For example, an unfilled node with no drawn boundary will only be active on its label. 388 | For other output, the active area is its bounding box. 389 | The active area for a cluster is its bounding box. 390 | For edges, the active areas are small circles where the edge contacts its head 391 | and tail nodes. In addition, for svg, cmapx and imap, the active area 392 | includes a thin polygon approximating the edge. The circles may 393 | overlap the related node, and the edge URL dominates. 394 | If the edge has a label, this will also be active. 395 | Finally, if the edge has a head or tail label, this will also be active. 396 | 397 | 398 | Note that, for edges, the attributes headURL, 399 | tailURL, labelURL and 400 | edgeURL allow control of various parts of an 401 | edge. Also note that, if active areas of two edges overlap, it is unspecified 402 | which area dominates. 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | Style of arrowhead on the head node of an edge. 413 | See also the dir attribute, 414 | and the undirected note. 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | Multiplicative scale factor for arrowheads. 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | Style of arrowhead on the tail node of an edge. 435 | See also the dir attribute, 436 | and the undirected note. 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | Bounding box of drawing in integer points. 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | When attached to the root graph, this color is used as the background for 457 | entire canvas. When a cluster attribute, it is used as the initial 458 | background for the cluster. If a cluster has a filled 459 | style, the 460 | cluster's fillcolor will overlay the 461 | background color. 462 | 463 | 464 | If no background color is specified for the root graph, no graphics 465 | operation are performed on the background. This works fine for 466 | PostScript but for bitmap output, all bits are initialized to something. 467 | This means that when the bitmap output is included in some other 468 | document, all of the bits within the bitmap's bounding box will be 469 | set, overwriting whatever color or graphics where already on the page. 470 | If this effect is not desired, and you only want to set bits explicitly 471 | assigned in drawing the graph, set bgcolor="transparent". 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | If true, the drawing is centered in the output canvas. 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | Specifies the character encoding used when interpreting string input 492 | as a text label. The default value is UTF-8. 493 | The other legal value is iso-8859-1 or, 494 | equivalently, 495 | Latin1. The charset attribute is case-insensitive. 496 | Note that if the character encoding used in the input does not 497 | match the charset value, the resulting output may be very strange. 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | Mode used for handling clusters. If clusterrank is local, a 508 | subgraph whose name begins with "cluster" is given special treatment. 509 | The subgraph is laid out separately, and then integrated as a unit into 510 | its parent graph, with a bounding rectangle drawn about it. 511 | If the cluster has a label parameter, this label 512 | is displayed within the rectangle. 513 | Note also that there can be clusters within clusters. 514 | At present, the modes global and none 515 | appear to be identical, both turning off the special cluster processing. 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | Basic drawing color for graphics, not text. For the latter, use the 526 | fontcolor attribute. 527 | 528 | 529 | For edges, the value 530 | can either be a single color or a colorList. 531 | In the latter case, the edge is drawn using parallel splines or lines, 532 | one for each color in the list, in the order given. 533 | The head arrow, if any, is drawn using the first color in the list, 534 | and the tail arrow, if any, the second color. This supports the common 535 | case of drawing opposing edges, but using parallel splines instead of 536 | separately routed multiedges. 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | This attribute specifies a color scheme namespace. If defined, it specifies 547 | the context for interpreting color names. In particular, if a 548 | color value has form xxx or //xxx, 549 | then the color xxx will be evaluated according to the current color scheme. 550 | If no color scheme is set, the standard X11 naming is used. 551 | For example, if colorscheme=bugn9, then color=7 552 | is interpreted as /bugn9/7. 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | Comments are inserted into output. Device-dependent. 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | If true, allow edges between clusters. (See lhead and ltail below.) 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | If true, use edge concentrators. 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | If false, the edge is not used in ranking the nodes. 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | If true, attach edge label to edge by a 2-segment 603 | polyline, underlining the label, then going to the closest point of spline. 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | This specifies the distance between nodes in separate connected 614 | components. If set too small, connected components may overlap. 615 | Only applicable if pack=false. 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | Set the number of dimensions used for the layout. The maximum value 626 | allowed is 10. 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | Set edge type for drawing arrowheads. This indicates which ends of the 637 | edge should be decorated with an arrowhead. The actual style of the 638 | arrowhead can be specified using the arrowhead 639 | and arrowtail attributes. 640 | See undirected. 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | Only valid when mode="ipsep". 651 | If true, constraints are generated for each edge in the largest (heuristic) 652 | directed acyclic subgraph such that the edge must point downwards. 653 | If hier, generates level constraints similar to those used with 654 | mode="hier". The main difference is that, in the latter 655 | case, only these constraints are involved, so a faster solver can be used. 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | Distortion factor for shape=polygon. 666 | Positive values cause top part to 667 | be larger than bottom; negative values do the opposite. 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | This specifies the expected number of pixels per inch on a display device. 678 | For bitmap output, this guarantees that text rendering will be 679 | done more accurately, both in size and in placement. For SVG output, 680 | it is used to guarantee that the dimensions in the output correspond to 681 | the correct number of points or inches. 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | If edgeURL is defined, this is the link used for the non-label 692 | parts of an edge. This value overrides any URL 693 | defined for the edge. 694 | Also, this value is used near the head or tail node unless overridden 695 | by a headURL or tailURL value, 696 | respectively. 697 | See undirected. 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | Synonym for edgeURL. 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | If the edge has a URL or edgeURL 718 | attribute, this attribute determines which window of the 719 | browser is used 720 | for the URL attached to the non-label part of the edge. 721 | Setting it to "_graphviz" will open a new window if it 722 | doesn't already exist, or reuse it if it does. 723 | If undefined, the value of the target is used. 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | Tooltip annotation attached to the non-label part of an edge. 734 | This is used only if the edge has a URL 735 | or edgeURL attribute. 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | Terminating condition. If the length squared of all energy gradients are 746 | < epsilon, the algorithm stops. 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | Fraction to increase polygons (multiply 757 | coordinates by 1 + esep) for purposes of spline edge routing. 758 | This should normally be strictly less than 759 | sep. 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | Color used to fill the background of a node or cluster 770 | assuming style=filled. 771 | If fillcolor is not defined, color is 772 | used. (For clusters, if color is not defined, 773 | bgcolor is used.) If this is not defined, 774 | the default is used, except for 775 | shape=point or when the output 776 | format is MIF, 777 | which use black by default. 778 | 779 | 780 | Note that a cluster inherits the root graph's attributes if defined. 781 | Thus, if the root graph has defined a fillcolor, this will override a 782 | color or bgcolor attribute set for the cluster. 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | If true, the node size is specified by the values of the 793 | width 794 | and height attributes only 795 | and is not expanded to contain the text label. 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | Color used for text. 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | Font used for text. This very much depends on the output format and, for 816 | non-bitmap output such as PostScript or SVG, the availability of the font 817 | when the graph is displayed or printed. As such, it is best to rely on 818 | font faces that are generally available, such as Times-Roman, Helvetica or 819 | Courier. 820 | 821 | 822 | If Graphviz was built using the 823 | fontconfig library, the latter library 824 | will be used to search for the font. However, if the fontname string 825 | contains a slash character "/", it is treated as a pathname for the font 826 | file, though font lookup will append the usual font suffixes. 827 | 828 | 829 | If Graphviz does not use fontconfig, fontname will be 830 | considered the name of a Type 1 or True Type font file. 831 | If you specify fontname=schlbk, the tool will look for a 832 | file named schlbk.ttf or schlbk.pfa or schlbk.pfb 833 | in one of the directories specified by 834 | the fontpath attribute. 835 | The lookup does support various aliases for the common fonts. 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | Allows user control of how basic fontnames are represented in SVG output. 846 | If fontnames is undefined or svg, 847 | the output will try to use known SVG fontnames. For example, the 848 | default font Times-Roman will be mapped to the 849 | basic SVG font serif. This can be overridden by setting 850 | fontnames to ps or gd. 851 | In the former case, known PostScript font names such as 852 | Times-Roman will be used in the output. 853 | In the latter case, the fontconfig font conventions 854 | are used. Thus, Times-Roman would be treated as 855 | Nimbus Roman No9 L. These last two options are useful 856 | with SVG viewers that support these richer fontname spaces. 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | Directory list used by libgd to search for bitmap fonts if Graphviz 867 | was not built with the fontconfig library. 868 | If fontpath is not set, the environment 869 | variable DOTFONTPATH is checked. 870 | If that is not set, GDFONTPATH is checked. 871 | If not set, libgd uses its compiled-in font path. 872 | Note that fontpath is an attribute of the root graph. 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | Font size, in points, used for text. 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | If the end points of an edge belong to the same group, i.e., have the 893 | same group attribute, parameters are set to avoid crossings and keep 894 | the edges straight. 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | If headURL is defined, it is 905 | output as part of the head label of the edge. 906 | Also, this value is used near the head node, overriding any 907 | URL value. 908 | See undirected. 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | If true, the head of an edge is clipped to the boundary of the head node; 919 | otherwise, the end of the edge goes to the center of the node, or the 920 | center of a port, if applicable. 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | Synonym for headURL. 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | Text label to be placed near head of edge. 941 | See undirected. 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | Indicates where on the head node to attach the head of the edge. 952 | In the default case, the edge is aimed towards the center of the node, 953 | and then clipped at the node boundary. 954 | See undirected. 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | If the edge has a headURL, 965 | this attribute determines which window of the 966 | browser is used 967 | for the URL. Setting it to "_graphviz" will open a new window if it 968 | doesn't already exist, or reuse it if it does. 969 | If undefined, the value of the target is used. 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | Tooltip annotation attached to the head of an edge. This is used only 980 | if the edge has a headURL attribute. 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | Height of node, in inches. This is taken as the initial, minimum height 991 | of the node. If fixedsize is true, this 992 | will be the final height of the node. Otherwise, if the node label 993 | requires more height to fit, the node's height will be increased to 994 | contain the label. Note also that, if the output format is dot, the 995 | value given to height will be the final value. 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | Synonym for URL. 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | Gives the name of a file containing an image to be displayed inside 1016 | a node. The image file must be in one of the recognized formats, 1017 | typically JPEG, PNG, GIF or Postscript, and be able to be converted 1018 | into the desired output format. 1019 | 1020 | 1021 | Unlike with the shapefile attribute, 1022 | the image is treated as node 1023 | content rather than the entire node. In particular, an image can 1024 | be contained in a node of any shape, not just a rectangle. 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | Attribute controlling how an image fills its 1035 | containing node. In general, the image is given its natural size, 1036 | (cf. dpi), 1037 | and the node size is made large enough to contain its image, its 1038 | label, its margin, and its peripheries. 1039 | Its width and height will also be at least as large as its 1040 | minimum width and height. 1041 | If, however, fixedsize=true, 1042 | the width and height attributes specify the exact size of the node. 1043 | 1044 | 1045 | During rendering, in the default case (imagescale=false), 1046 | the image retains its natural size. 1047 | If true, 1048 | the image is uniformly scaled (i.e., its aspect ratio is 1049 | preserved) to fit inside the node. 1050 | At least one dimension of the image will be as large as possible 1051 | given the size of the node. 1052 | When width, 1053 | the width of the image is scaled to fill the node width. 1054 | The corresponding property holds when imagescale=height. 1055 | When both, 1056 | both the height and the width are scaled separately to fill the node. 1057 | 1058 | 1059 | In all cases, if a dimension of the image is larger than the 1060 | corresponding dimension of the node, that dimension of the 1061 | image is scaled down to fit the node. As with the case of 1062 | expansion, if imagescale=true, width and height are 1063 | scaled uniformly. 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | Text label attached to objects. 1074 | If a node's shape is record, then the label can 1075 | have a special format 1076 | which describes the record layout. 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | If labelURL is defined, this is the link used for the label 1087 | of an edge. This value overrides any URL 1088 | defined for the edge. 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | This, along with labeldistance, determine 1099 | where the 1100 | headlabel (taillabel) are placed with respect to the head (tail) 1101 | in polar coordinates. The origin in the coordinate system is 1102 | the point where the edge touches the node. The ray of 0 degrees 1103 | goes from the origin back along the edge, parallel to the edge 1104 | at the origin. 1105 | 1106 | 1107 | The angle, in degrees, specifies the rotation from the 0 degree ray, 1108 | with positive angles moving counterclockwise and negative angles 1109 | moving clockwise. 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | Multiplicative scaling factor adjusting the distance that 1120 | the headlabel (taillabel) is from the head (tail) node. 1121 | The default distance is 10 points. See labelangle 1122 | for more details. 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | If true, allows edge labels to be less constrained in position. 1133 | In particular, it may appear on top of other edges. 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | Color used for headlabel and taillabel. 1144 | If not set, defaults to edge's fontcolor. 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | Font used for headlabel and taillabel. 1155 | If not set, defaults to edge's fontname. 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | Font size, in points, used for headlabel and taillabel. 1166 | If not set, defaults to edge's fontsize. 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | Synonym for labelURL. 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | Justification for cluster labels. If r, the label 1187 | is right-justified within bounding rectangle; if l, left-justified; 1188 | else the label is centered. 1189 | Note that a subgraph inherits attributes from its parent. Thus, if 1190 | the root graph sets labeljust to l, the subgraph inherits 1191 | this value. 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | Top/bottom placement of graph and cluster labels. 1202 | If the attribute is t, place label at the top; 1203 | if the attribute is b, place label at the bottom. 1204 | By default, root 1205 | graph labels go on the bottom and cluster labels go on the top. 1206 | Note that a subgraph inherits attributes from its parent. Thus, if 1207 | the root graph sets labelloc to b, the subgraph inherits 1208 | this value. 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | If the edge has a URL or labelURL 1219 | attribute, this attribute determines which window of the 1220 | browser is used 1221 | for the URL attached to the label. 1222 | Setting it to "_graphviz" will open a new window if it 1223 | doesn't already exist, or reuse it if it does. 1224 | If undefined, the value of the target is used. 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | Tooltip annotation attached to label of an edge. 1235 | This is used only if the edge has a URL 1236 | or labelURL attribute. 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | If true, the graph is rendered in landscape mode. Synonymous with 1247 | rotate=90 or 1248 | orientation=landscape. 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | Specifies layers in which the node or edge is present. 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | Specifies a linearly ordered list of layer names attached to the graph 1269 | The graph is then output in separate layers. Only those components 1270 | belonging to the current output layer appear. For more information, 1271 | see the page How to use drawing layers (overlays). 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | Specifies the separator characters used to split the 1282 | layers attribute into a list of layer names. 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | Specifies the name of the layout algorithm to use, such as dot 1293 | or neato. Normally, graphs should be kept independent of a type of 1294 | layout. In some cases, however, it can be convenient to embed the type 1295 | of layout desired within the graph. For example, a graph containing 1296 | position information from a layout might want to record what the 1297 | associated layout algorithm was. 1298 | 1299 | 1300 | This attribute takes precedence over 1301 | the -K flag 1302 | or the actual command name used. 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | Preferred edge length, in inches. 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | Specifies strictness of level constraints in neato 1323 | when mode="ipsep" or "hier". 1324 | Larger positive values mean stricter constraints, which demand more 1325 | separation between levels. On the other hand, negative values will relax 1326 | the constraints by allowing some overlap between the levels. 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | Logical head of an edge. When compound is true, 1337 | if lhead is defined and is the name of a cluster containing 1338 | the real head, 1339 | the edge is clipped to the boundary of the cluster. 1340 | See undirected. 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | Label position, in points. 1351 | The position indicates the center of the label. 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | Logical tail of an edge. When compound is true, 1362 | if ltail is defined and is the name of a cluster 1363 | containing the real tail, 1364 | the edge is clipped to the boundary of the cluster. 1365 | See undirected. 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | For graphs, this sets x and y margins of canvas, in inches. If the margin 1376 | is a single double, both margins are set equal to the given value. 1377 | 1378 | 1379 | Note that the margin is not part of the drawing but just empty space 1380 | left around the drawing. It basically corresponds to a translation of 1381 | drawing, as would be necessary to center a drawing on a page. Nothing 1382 | is actually drawn in the margin. To actually extend the background of 1383 | a drawing, see the pad attribute. 1384 | 1385 | 1386 | For nodes, this attribute specifies space left around the node's label. 1387 | By default, the value is 0.11,0.055. 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | Sets the number of iterations used. 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | Multiplicative scale factor used to alter the MinQuit (default = 8) 1408 | and MaxIter (default = 24) parameters used during crossing 1409 | minimization. These correspond to the 1410 | number of tries without improvement before quitting and the 1411 | maximum number of iterations in each pass. 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | Specifies the minimum separation between all nodes. 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | 1430 | 1431 | Minimum edge length (rank difference between head and tail). 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | Technique for optimizing the layout. If mode is major, 1442 | neato uses stress majorization. If mode is KK, 1443 | neato uses a version of the gradient descent method. The only advantage 1444 | to the latter technique is that it is sometimes appreciably faster for 1445 | small (number of nodes < 100) graphs. A significant disadvantage is that 1446 | it may cycle. 1447 | 1448 | 1449 | There are two new, experimental modes in neato, hier, which adds a top-down 1450 | directionality similar to the layout used in dot, and ipsep, which 1451 | allows the graph to specify minimum vertical and horizontal distances 1452 | between nodes. (See the sep attribute.) 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | This value specifies how the distance matrix is computed for the input 1463 | graph. The distance matrix specifies the ideal distance between every 1464 | pair of nodes. neato attemps to find a layout which best achieves 1465 | these distances. By default, it uses the length of the shortest path, 1466 | where the length of each edge is given by its len 1467 | attribute. If model is circuit, neato uses the 1468 | circuit resistance 1469 | model to compute the distances. This tends to emphasize clusters. If 1470 | model is subset, neato uses the subset model. This sets the 1471 | edge length to be the number of nodes that are neighbors of exactly one 1472 | of the end points, and then calculates the shortest paths. This helps 1473 | to separate nodes with high degree. 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | If Graphviz is built with MOSEK defined, mode=ipsep and mosek=true, 1484 | the Mosek software (www.mosek.com) is use to solve the ipsep constraints. 1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | Minimum space between two adjacent nodes in the same rank, in inches. 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | By default, the justification of multi-line labels is done within the 1505 | largest context that makes sense. Thus, in the label of a polygonal 1506 | node, a left-justified line will align with the left side of the node 1507 | (shifted by the prescribed margin). 1508 | In record nodes, left-justified 1509 | line will line up with the left side of the enclosing column of fields. 1510 | If nojustify is true, multi-line labels will be justified 1511 | in the context of itself. For example, if the attribute is set, 1512 | the first label line is long, and the second is shorter and left-justified, 1513 | the second will align with the left-most character in the first line, 1514 | regardless of how large the node might be. 1515 | 1516 | 1517 | 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | If set, normalize coordinates of final 1525 | layout so that the first point is at the origin, and then rotate the 1526 | layout so that the first edge is horizontal. 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | Used to set number of iterations in 1537 | network simplex applications, used in 1538 | computing node x coordinates. 1539 | If defined, # iterations = nslimit * # nodes; 1540 | otherwise, # iterations = MAXINT. 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | Used to set number of iterations in 1551 | network simplex applications, used for ranking nodes. 1552 | If defined, # iterations = nslimit1 * # nodes; 1553 | otherwise, # iterations = MAXINT. 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | If "out" for a graph G, and n is a node in G, then edges n->* appear 1564 | left-to-right in the same order in which they are defined. 1565 | If "in", the edges *->n appear 1566 | left-to-right in the same order in which they are defined for all 1567 | nodes n. 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | Specify order in which nodes and edges are drawn. 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | Determines if and how node overlaps should be removed. Nodes are first 1607 | enlarged using the sep attribute. 1608 | If true, overlaps are retained. 1609 | If the value is scale, overlaps are removed by uniformly scaling in x and y. 1610 | If the value converts to false, node overlaps are removed by a 1611 | Voronoi-based technique. 1612 | If the value is scalexy, x and y are separately 1613 | scaled to remove overlaps. 1614 | If the value is orthoxy or orthoyx, overlaps 1615 | are moved by optimizing two constraint problems, one for the x axis and 1616 | one for the y. The suffix indicates which axis is processed first. 1617 | If the value is ortho, the technique is similar to orthoxy except a 1618 | heuristic is used to reduce the bias between the two passes. 1619 | If the value is ortho_yx, the technique is the same as ortho, except 1620 | the roles of x and y are reversed. 1621 | The values portho, porthoxy, porthoxy, and portho_yx are similar 1622 | to the previous four, except only pseudo-orthogonal ordering is 1623 | enforced. 1624 | 1625 | 1626 | If the value is compress, the layout will be scaled down as much as 1627 | possible without introducing any overlaps, obviously assuming there are 1628 | none to begin with. 1629 | 1630 | 1631 | If the value is ipsep, and the layout is done by neato with 1632 | mode="ipsep", the overlap removal constraints are 1633 | incorporated into the layout algorithm itself. 1634 | N.B. At present, this only supports one level of clustering. 1635 | 1636 | 1637 | If the value is vpsc, overlap removal is similarly to ortho, except 1638 | quadratic optimization is used to minimize node displacement. 1639 | N.B. At present, this mode only works when mode="ipsep". 1640 | 1641 | 1642 | Except for fdp, the layouts assume overlap="true" as the default. 1643 | Fdp first uses a number of passes using built-in, force-directed technique 1644 | to remove overlaps. Thus, fdp accepts overlap with an integer 1645 | prefix followed by a colon, specifying the number of tries. If there is 1646 | no prefix, no initial tries will be performed. If there is nothing following 1647 | a colon, none of the above methods will be attempted. By default, fdp 1648 | uses overlap="9:portho". Note that overlap="true", 1649 | overlap="0:true" and overlap="0:" all turn off all overlap 1650 | removal. 1651 | 1652 | 1653 | Except for the Voronoi method, all of these transforms preserve the 1654 | orthogonal ordering of the original layout. That is, if the x coordinates 1655 | of two nodes are originally the same, they will remain the same, and if 1656 | the x coordinate of one node is originally less than the x coordinate of 1657 | another, this relation will still hold in the transformed layout. The 1658 | similar properties hold for the y coordinates. 1659 | This is not quite true for the "porth*" cases. For these, orthogonal 1660 | ordering is only preserved among nodes related by an edge. 1661 | 1662 | 1663 | NOTEThe methods orthoxy and orthoyx are still evolving. The semantics of these may change, or these methods may disappear altogether. 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | This is true if the value of pack is true (case-insensitive) or a 1674 | non-negative integer. If true, each connected component of the graph is 1675 | laid out separately, and then the graphs are packed tightly. 1676 | If pack has an integral value, this is used as the size, 1677 | in points, of 1678 | a margin around each part; otherwise, a default margin of 8 is used. 1679 | If pack is interpreted as false, the entire graph is laid out together. 1680 | The granularity and method of packing is influenced by the 1681 | packmode attribute. 1682 | 1683 | 1684 | For layouts which always do packing, such a twopi, the pack 1685 | attribute is just used to set the margin. 1686 | 1687 | 1688 | 1689 | 1690 | 1691 | 1692 | 1693 | 1694 | 1695 | This indicates the granularity and method used for packing 1696 | (cf. packMode). Note that defining 1697 | packmode will automatically turn on packing as though one had 1698 | set pack=true. 1699 | 1700 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | 1707 | 1708 | The pad attribute specifies how much, in inches, to extend the 1709 | drawing area around the minimal area needed to draw the graph. 1710 | If the pad is a single double, both the x and y pad values are set 1711 | equal to the given value. This area is part of the 1712 | drawing and will be filled with the background color, if appropriate. 1713 | 1714 | 1715 | Normally, a small pad is used for aesthetic reasons, especially when 1716 | a background color is used, to avoid having nodes and edges abutting 1717 | the boundary of the drawn region. 1718 | 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | Width and height of output pages, in inches. If this is set and is 1728 | smaller than the size of the layout, a rectangular array of pages of 1729 | the specified page size is overlaid on the layout, with origins 1730 | aligned in the lower-left corner, thereby partitioning the layout 1731 | into pages. The pages are then produced one at a time, in 1732 | pagedir order. 1733 | 1734 | 1735 | At present, this only works for PostScript output. For other types of 1736 | output, one should use another tool to split the output into multiple 1737 | output files. Or use the viewport to generate 1738 | multiple files. 1739 | 1740 | 1741 | 1742 | 1743 | 1744 | 1745 | 1746 | 1747 | 1748 | If the page attribute is set and applicable, 1749 | this attribute specifies the order in which the pages are emitted. 1750 | This is limited to one of the 8 row or column major orders. 1751 | 1752 | 1753 | 1754 | 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | Color used to draw the bounding box around a cluster. 1761 | If pencolor is not defined, color is 1762 | used. If this is not defined, bgcolor is used. 1763 | If this is not defined, the default is used. 1764 | 1765 | 1766 | Note that a cluster inherits the root graph's attributes if defined. 1767 | Thus, if the root graph has defined a pencolor, this will override a 1768 | color or bgcolor attribute set for the cluster. 1769 | 1770 | 1771 | 1772 | 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | Set number of peripheries used in polygonal shapes and cluster 1779 | boundaries. Note that 1780 | user-defined shapes are treated as a 1781 | form of box shape, so the default 1782 | peripheries value is 1 and the user-defined shape will be drawn in 1783 | a bounding rectangle. Setting peripheries=0 will turn this off. 1784 | Also, 1 is the maximum peripheries value for clusters. 1785 | 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | If true and the node has a pos attribute on input, neato prevents the 1795 | node from moving from the input position. This property can also be specified 1796 | in the pos attribute itself (cf. the point type). 1797 | 1798 | 1799 | 1800 | 1801 | 1802 | 1803 | 1804 | 1805 | 1806 | Position of node, or spline control points. 1807 | For nodes, the position indicates the center of the node. 1808 | On output, the coordinates are in points. 1809 | 1810 | 1811 | In neato and fdp, pos can be used to set the initial position of a node. 1812 | By default, the coordinates are assumed to be in inches. However, the 1813 | -s command line flag can be used to specify 1814 | different units. 1815 | 1816 | 1817 | When the -n command line flag is used with 1818 | neato, it is assumed the positions have been set by one of the layout 1819 | programs, and are therefore in points. Thus, neato -n can accept 1820 | input correctly without requiring a -s flag and, in fact, 1821 | ignores any such flag. 1822 | 1823 | 1824 | 1825 | 1826 | 1827 | 1828 | 1829 | 1830 | 1831 | If quantum > 0.0, node label dimensions 1832 | will be rounded to integral multiples of the quantum. 1833 | 1834 | 1835 | 1836 | 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | Rank constraints on the nodes in a subgraph. 1843 | If same, all nodes are placed on the same rank. 1844 | If min, all nodes are placed on the minimum rank. 1845 | If source, all nodes are placed on the minimum rank, and 1846 | the only nodes on the minimum rank belong to some subgraph whose 1847 | rank attribute is "source" or "min". 1848 | Analogous criteria hold for rank=max and rank=sink. 1849 | (Note: the 1850 | minimum rank is topmost or leftmost, and the maximum rank is bottommost 1851 | or rightmost.) 1852 | 1853 | 1854 | 1855 | 1856 | 1857 | 1858 | 1859 | 1860 | 1861 | Sets direction of graph layout. For example, if rankdir="LR", 1862 | and barring cycles, an edge T -> H; will go 1863 | from left to right. By default, graphs are laid out from top to bottom. 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | 1870 | 1871 | 1872 | 1873 | In dot, this gives the desired rank separation, in inches. This is 1874 | the minimum vertical distance between the bottom of the nodes in one 1875 | rank and the tops of nodes in the next. If the value 1876 | contains "equally", the centers of all ranks are spaced equally apart. 1877 | Note that both 1878 | settings are possible, e.g., ranksep = "1.2 equally". 1879 | In twopi, specifies radial separation of concentric circles. 1880 | 1881 | 1882 | 1883 | 1884 | 1885 | 1886 | 1887 | 1888 | 1889 | Sets the aspect ratio (drawing height/drawing width) for the drawing. 1890 | Note that this is adjusted before 1891 | the size attribute constraints are enforced. 1892 | 1893 | 1894 | If ratio is numeric, it is taken as the desired aspect ratio. 1895 | Then, if the actual aspect ratio is less than the desired ratio, 1896 | the drawing height is scaled up to achieve the 1897 | desired ratio; if the actual ratio is greater than that desired ratio, 1898 | the drawing width is scaled up. 1899 | 1900 | 1901 | If ratio = fill and the size 1902 | attribute is set, node positions are scaled, separately in both x 1903 | and y, so that the final drawing exactly fills the specified size. 1904 | 1905 | 1906 | If ratio = compress and the size 1907 | attribute is set, dot attempts to compress the initial layout to fit 1908 | in the given size. This achieves a tighter packing of nodes but 1909 | reduces the balance and symmetry. This feature only works in dot. 1910 | 1911 | 1912 | If ratio = expand, the size 1913 | attribute is set, and both the width and the height of the graph are 1914 | less than the value in size, node positions are scaled 1915 | uniformly until at least 1916 | one dimension fits size exactly. 1917 | Note that this is distinct from using size as the 1918 | desired size, as here the drawing is expanded before edges are generated and 1919 | all node and text sizes remain unchanged. 1920 | 1921 | 1922 | If ratio = auto, the page 1923 | attribute is set and the graph cannot be drawn on a single page, 1924 | then size is set to an ``ideal'' value. 1925 | In particular, the size in a given dimension will be the smallest integral 1926 | multiple of the page size in that dimension which is at least half the 1927 | current size. The two dimensions are then scaled independently to the 1928 | new size. This feature only works in dot. 1929 | 1930 | 1931 | 1932 | 1933 | 1934 | 1935 | 1936 | 1937 | 1938 | Rectangles for fields of records, in points. 1939 | 1940 | 1941 | 1942 | 1943 | 1944 | 1945 | 1946 | 1947 | 1948 | If true, force polygon to be regular. 1949 | 1950 | 1951 | 1952 | 1953 | 1954 | 1955 | 1956 | 1957 | 1958 | If true and there are multiple clusters, run cross 1959 | minimization a second time. 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1968 | 1969 | This is a synonym for the dpi attribute. 1970 | 1971 | 1972 | 1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | This specifies nodes to be used as the center of the 1980 | layout and the root of the generated spanning tree. As a graph attribute, 1981 | this gives the name of the node. As a node attribute (circo only), it 1982 | specifies that the node should be used as a central node. In twopi, 1983 | this will actually be the central node. In circo, the block containing 1984 | the node will be central in the drawing of its connected component. 1985 | If not defined, 1986 | twopi will pick a most central node, and circo will pick a random node. 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996 | If 90, set drawing orientation to landscape. 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | Edges with the same head and the same samehead value are aimed 2007 | at the same point on the head. 2008 | See undirected. 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | Edges with the same tail and the same sametail value are aimed 2019 | at the same point on the tail. 2020 | See undirected. 2021 | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 | 2030 | If the input graph defines the vertices 2031 | attribute, and output is dot or xdot, this gives 2032 | the number of points used for a node whose shape is a circle or ellipse. 2033 | It plays the same role in neato, when adjusting the layout to avoid 2034 | overlapping nodes, and in image maps. 2035 | 2036 | 2037 | 2038 | 2039 | 2040 | 2041 | 2042 | 2043 | 2044 | During network simplex, maximum number of edges with negative cut values 2045 | to search when looking for one with minimum cut value. 2046 | 2047 | 2048 | 2049 | 2050 | 2051 | 2052 | 2053 | 2054 | 2055 | Fraction to increase polygons (multiply 2056 | coordinates by 1 + sep) for purposes of determining overlap. Guarantees 2057 | a minimal non-zero distance between nodes. 2058 | If unset but esep is defined, sep will be 2059 | set to esep/0.8. If esep is unset, the default value 2060 | is used. 2061 | 2062 | 2063 | When overlap="ipsep" or "vpsc", 2064 | sep gives a minimum distance, in inches, to be left between nodes. 2065 | In this case, if sep is a pointf, the x and y separations can be 2066 | specified separately. 2067 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | Set the shape of a node. 2077 | 2078 | 2079 | 2080 | 2081 | 2082 | 2083 | 2084 | 2085 | 2086 | (Deprecated) If defined, shapefile specifies a file containing user-supplied node content. 2087 | The shape of the node is set to box. 2088 | The image in the shapefile must be 2089 | rectangular. The image formats supported as well as the precise semantics of 2090 | how the file is used depends on the 2091 | output format. For further details, see 2092 | External PostScript files. 2093 | 2094 | 2095 | There is one exception to this usage. 2096 | If shape is set to "epsf", shapefile gives 2097 | a filename containing a definition of the node in PostScript. 2098 | The graphics defined must be contain all of the 2099 | node content, including any desired boundaries. 2100 | For further details, see 2101 | 2102 | External PostScript files. 2103 | 2104 | 2105 | 2106 | 2107 | 2108 | 2109 | 2110 | 2111 | 2112 | Print guide boxes in PostScript at the beginning of 2113 | routesplines if 1, or at the end if 2. (Debugging) 2114 | 2115 | 2116 | 2117 | 2118 | 2119 | 2120 | 2121 | 2122 | 2123 | Number of sides if shape=polygon. 2124 | 2125 | 2126 | 2127 | 2128 | 2129 | 2130 | 2131 | 2132 | 2133 | Maximum width and height of drawing, in inches. 2134 | If defined and the drawing is too large, the drawing is uniformly 2135 | scaled down so that it fits within the given size. 2136 | 2137 | 2138 | If size ends in an exclamation point (!), 2139 | then it is taken to be 2140 | the desired size. In this case, if both dimensions of the drawing are 2141 | less than size, the drawing is scaled up uniformly until at 2142 | least one dimension equals its dimension in size. 2143 | 2144 | 2145 | Note that there is some interaction between the size and 2146 | ratio attributes. 2147 | 2148 | 2149 | 2150 | 2151 | 2152 | 2153 | 2154 | 2155 | 2156 | Skew factor for shape=polygon. Positive values 2157 | skew top of polygon to right; negative to left. 2158 | 2159 | 2160 | 2161 | 2162 | 2163 | 2164 | 2165 | 2166 | 2167 | Controls how, and if, edges are represented. If true, edges are drawn as 2168 | splines routed around nodes; if false, edges are drawn as line segments. 2169 | If set to "", no edges are drawn at all. 2170 | 2171 | 2172 | (1 March 2007) The values line and spline can be 2173 | used as synonyms for false and true, respectively. 2174 | In addition, the value polyline specifies that edges should be 2175 | drawn as polylines. 2176 | 2177 | 2178 | By default, the attribute is unset. How this is interpreted depends on 2179 | the layout. For dot, the default is to draw edges as splines. For all 2180 | other layouts, the default is to draw edges as line segments. Note that 2181 | for these latter layouts, if splines="true", this 2182 | requires non-overlapping nodes (cf. overlap). 2183 | If fdp is used for layout and splines="compound", then the edges are 2184 | drawn to avoid clusters as well as nodes. 2185 | 2186 | 2187 | 2188 | 2189 | 2190 | 2191 | 2192 | 2193 | 2194 | Parameter used to determine the initial layout of nodes. If unset, the 2195 | nodes are randomly placed in a unit square with 2196 | the same seed is always used for the random number generator, so the 2197 | initial placement is repeatable. 2198 | 2199 | 2200 | 2201 | 2202 | 2203 | 2204 | 2205 | 2206 | 2207 | Set style for node or edge. For cluster subgraph, if "filled", the 2208 | cluster box's background is filled. 2209 | 2210 | 2211 | 2212 | 2213 | 2214 | 2215 | 2216 | 2217 | 2218 | A URL or pathname specifying an XML style sheet, used in SVG output. 2219 | 2220 | 2221 | 2222 | 2223 | 2224 | 2225 | 2226 | 2227 | 2228 | If tailURL is defined, it is 2229 | output as part of the tail label of the edge. 2230 | Also, this value is used near the tail node, overriding any 2231 | URL value. 2232 | See undirected. 2233 | 2234 | 2235 | 2236 | 2237 | 2238 | 2239 | 2240 | 2241 | 2242 | If true, the tail of an edge is clipped to the boundary of the tail node; 2243 | otherwise, the end of the edge goes to the center of the node, or the 2244 | center of a port, if applicable. 2245 | 2246 | 2247 | 2248 | 2249 | 2250 | 2251 | 2252 | 2253 | 2254 | Synonym for tailURL. 2255 | 2256 | 2257 | 2258 | 2259 | 2260 | 2261 | 2262 | 2263 | 2264 | Text label to be placed near tail of edge. 2265 | See undirected. 2266 | 2267 | 2268 | 2269 | 2270 | 2271 | 2272 | 2273 | 2274 | 2275 | Indicates where on the tail node to attach the tail of the edge. 2276 | See undirected. 2277 | 2278 | 2279 | 2280 | 2281 | 2282 | 2283 | 2284 | 2285 | 2286 | If the edge has a tailURL, 2287 | this attribute determines which window of the 2288 | browser is used 2289 | for the URL. Setting it to "_graphviz" will open a new window if it 2290 | doesn't already exist, or reuse it if it does. 2291 | If undefined, the value of the target is used. 2292 | 2293 | 2294 | 2295 | 2296 | 2297 | 2298 | 2299 | 2300 | 2301 | Tooltip annotation attached to the tail of an edge. This is used only 2302 | if the edge has a tailURL attribute. 2303 | 2304 | 2305 | 2306 | 2307 | 2308 | 2309 | 2310 | 2311 | 2312 | If the object has a URL, this attribute determines which window 2313 | of the browser is used for the URL. 2314 | See W3C documentation. 2315 | 2316 | 2317 | 2318 | 2319 | 2320 | 2321 | 2322 | 2323 | 2324 | Tooltip annotation attached to the node or edge. If unset, Graphviz 2325 | will use the object's label if defined. 2326 | Note that if the label is a record specification or an HTML-like 2327 | label, the resulting tooltip may be unhelpful. In this case, if 2328 | tooltips will be generated, the user should set a tooltip 2329 | attribute explicitly. 2330 | 2331 | 2332 | 2333 | 2334 | 2335 | 2336 | 2337 | 2338 | 2339 | If set explicitly to true or false, the value determines whether or not 2340 | internal bitmap rendering relies on a truecolor color model or uses 2341 | a color palette. 2342 | If the attribute is unset, truecolor is not used 2343 | unless there is a shapefile property 2344 | for some node in the graph. 2345 | The output model will use the input model when possible. 2346 | 2347 | 2348 | Use of color palettes results in less memory usage during creation of the 2349 | bitmaps and smaller output files. 2350 | 2351 | 2352 | Usually, the only time it is necessary to specify the truetype model 2353 | is if the graph uses more than 256 colors. 2354 | However, if one uses bgcolor=transparent with 2355 | a color palette, font 2356 | antialiasing can show up as a fuzzy white area around characters. 2357 | Using truecolor=true avoids this problem. 2358 | 2359 | 2360 | 2361 | 2362 | 2363 | 2364 | 2365 | 2366 | 2367 | If the input graph defines this attribute, the node is polygonal, 2368 | and output is dot or xdot, this attribute provides the 2369 | coordinates of the vertices of the node's polygon, in inches. 2370 | If the node is an ellipse or circle, the 2371 | samplepoints attribute affects 2372 | the output. 2373 | 2374 | 2375 | 2376 | 2377 | 2378 | 2379 | 2380 | 2381 | 2382 | Clipping window on final drawing. 2383 | 2384 | 2385 | 2386 | 2387 | 2388 | 2389 | 2390 | 2391 | 2392 | Factor to scale up drawing to allow margin for expansion in 2393 | Voronoi technique. dim' = (1+2*margin)*dim. 2394 | 2395 | 2396 | 2397 | 2398 | 2399 | 2400 | 2401 | 2402 | 2403 | Weight of edge. In dot, the heavier the weight, the shorter, 2404 | straighter and more vertical the edge is. In neato, the heavier the 2405 | weight, the more neato will try to place the end points so that the 2406 | length of the edge is len. 2407 | 2408 | 2409 | 2410 | 2411 | 2412 | 2413 | 2414 | 2415 | 2416 | Width of node, in inches. This is taken as the initial, minimum width 2417 | of the node. If fixedsize is true, this 2418 | will be the final width of the node. Otherwise, if the node label 2419 | requires more width to fit, the node's width will be increased to 2420 | contain the label. Note also that, if the output format is dot, the 2421 | value given to width will be the final value. 2422 | 2423 | 2424 | 2425 | 2426 | 2427 | 2428 | 2429 | 2430 | 2431 | Provides z coordinate value for 3D layouts and displays. If the 2432 | graph has dim set to 3 (or more), 2433 | neato will use a node's z value 2434 | for the z coordinate of its initial position if 2435 | its pos attribute is also defined. 2436 | 2437 | 2438 | Even if no z values are specified in the input, it is necessary to 2439 | declare a z attribute for nodes, e.g, using node[z=""] 2440 | in order to get z values on output. 2441 | Thus, setting dim=3 but not declaring z will 2442 | cause neato -Tvrml to 2443 | layout the graph in 3D but project the layout onto the xy-plane 2444 | for the rendering. If the z attribute is declared, the final rendering 2445 | will be in 3D. 2446 | 2447 | 2448 | 2449 | 2450 | 2451 | 2452 | 2453 | 2454 | 2455 | 2456 | 2457 | 2458 | 2459 | 2460 | 2461 | 2462 | 2463 | 2464 | 2465 | 2466 | 2467 | 2468 | 2469 | 2470 | 2471 | 2472 | 2473 | 2474 | 2475 | 2476 | 2477 | 2478 | 2479 | 2480 | 2481 | 2482 | 2483 | 2484 | 2485 | 2486 | 2487 | 2488 | 2489 | 2490 | 2491 | 2492 | 2493 | 2494 | 2495 | 2496 | 2497 | 2498 | 2499 | 2500 | 2501 | 2502 | 2503 | 2504 | 2505 | 2506 | 2507 | 2508 | 2509 | 2510 | 2511 | 2512 | 2513 | 2514 | 2515 | 2516 | 2517 | 2518 | 2519 | 2520 | 2521 | 2522 | 2523 | 2524 | 2525 | 2526 | 2527 | 2528 | 2529 | 2530 | 2531 | 2532 | 2533 | 2534 | 2535 | 2536 | 2537 | 2538 | 2539 | 2540 | 2541 | 2542 | 2543 | 2544 | 2545 | 2546 | 2547 | 2548 | 2549 | 2550 | 2551 | 2552 | 2553 | 2554 | 2555 | 2556 | 2557 | 2558 | 2559 | 2560 | 2561 | 2562 | 2563 | 2564 | 2565 | 2566 | 2567 | 2568 | 2569 | 2570 | 2571 | 2572 | 2573 | 2574 | 2575 | 2576 | 2577 | 2578 | 2579 | 2580 | 2581 | 2582 | 2583 | 2584 | 2585 | 2586 | 2587 | 2588 | 2589 | 2590 | 2591 | 2592 | 2593 | 2594 | 2595 | 2596 | 2597 | 2598 | 2599 | 2600 | 2601 | 2602 | 2603 | 2604 | 2605 | 2606 | 2607 | 2608 | 2609 | 2610 | 2611 | 2612 | 2613 | 2614 | 2615 | 2616 | 2617 | 2618 | 2619 | 2620 | 2621 | 2622 | 2623 | 2624 | 2625 | 2626 | 2627 | 2628 | 2629 | 2630 | 2631 | 2632 | 2633 | 2634 | 2635 | 2636 | 2637 | 2638 | 2639 | 2640 | 2641 | 2642 | 2643 | 2644 | 2645 | 2646 | 2647 | 2648 | 2649 | 2650 | 2651 | 2652 | 2653 | 2654 | 2655 | 2656 | 2657 | -------------------------------------------------------------------------------- /tests/phpDocumentor/GraphViz/Test/AttributeTest.php: -------------------------------------------------------------------------------- 1 | fixture = new Attribute('a', '1'); 33 | } 34 | 35 | /** 36 | * Tests the construct method 37 | * 38 | * @covers \phpDocumentor\GraphViz\Attribute::__construct 39 | * @returnn void 40 | */ 41 | public function testConstruct(): void 42 | { 43 | $fixture = new Attribute('MyKey', 'MyValue'); 44 | $this->assertInstanceOf( 45 | Attribute::class, 46 | $fixture 47 | ); 48 | $this->assertSame('MyKey', $fixture->getKey()); 49 | $this->assertSame('MyValue', $fixture->getValue()); 50 | } 51 | 52 | /** 53 | * Tests the getting and setting of the key. 54 | * 55 | * @covers \phpDocumentor\GraphViz\Attribute::getKey 56 | * @covers \phpDocumentor\GraphViz\Attribute::setKey 57 | */ 58 | public function testKey(): void 59 | { 60 | $this->assertSame( 61 | $this->fixture->getKey(), 62 | 'a', 63 | 'Expecting the key to match the initial state' 64 | ); 65 | $this->assertSame( 66 | $this->fixture, 67 | $this->fixture->setKey('b'), 68 | 'Expecting a fluent interface' 69 | ); 70 | $this->assertSame( 71 | $this->fixture->getKey(), 72 | 'b', 73 | 'Expecting the key to contain the new value' 74 | ); 75 | } 76 | 77 | /** 78 | * Tests the getting and setting of the value. 79 | * 80 | * @covers \phpDocumentor\GraphViz\Attribute::getValue 81 | * @covers \phpDocumentor\GraphViz\Attribute::setValue 82 | */ 83 | public function testValue(): void 84 | { 85 | $this->assertSame( 86 | $this->fixture->getValue(), 87 | '1', 88 | 'Expecting the value to match the initial state' 89 | ); 90 | $this->assertSame( 91 | $this->fixture, 92 | $this->fixture->setValue('2'), 93 | 'Expecting a fluent interface' 94 | ); 95 | $this->assertSame( 96 | $this->fixture->getValue(), 97 | '2', 98 | 'Expecting the value to contain the new value' 99 | ); 100 | } 101 | 102 | /** 103 | * Tests whether a string starting with a < is recognized as HTML. 104 | * 105 | * @covers \phpDocumentor\GraphViz\Attribute::isValueInHtml 106 | */ 107 | public function testIsValueInHtml(): void 108 | { 109 | $this->fixture->setValue('a'); 110 | $this->assertFalse( 111 | $this->fixture->isValueInHtml(), 112 | 'Expected value to not be a HTML code' 113 | ); 114 | 115 | $this->fixture->setValue('test'); 116 | $this->assertTrue( 117 | $this->fixture->isValueInHtml(), 118 | 'Expected value to be recognized as a HTML code' 119 | ); 120 | } 121 | 122 | /** 123 | * Tests whether the toString provides a valid GraphViz attribute string. 124 | * 125 | * @covers \phpDocumentor\GraphViz\Attribute::__toString 126 | */ 127 | public function testToString(): void 128 | { 129 | $this->fixture = new Attribute('a', 'b'); 130 | $this->assertSame( 131 | 'a="b"', 132 | (string) $this->fixture, 133 | 'Strings should be surrounded with quotes' 134 | ); 135 | 136 | $this->fixture->setValue('a"a'); 137 | $this->assertSame( 138 | 'a="a\"a"', 139 | (string) $this->fixture, 140 | 'Strings should be surrounded with quotes' 141 | ); 142 | 143 | $this->fixture->setKey('url'); 144 | $this->assertSame( 145 | 'URL="a\"a"', 146 | (string) $this->fixture, 147 | 'The key named URL must be uppercased' 148 | ); 149 | 150 | $this->fixture->setValue('test'); 151 | $this->assertSame( 152 | 'URL=test', 153 | (string) $this->fixture, 154 | 'HTML strings should not be surrounded with quotes' 155 | ); 156 | } 157 | 158 | /** 159 | * Tests whether the toString provides a valid GraphViz attribute string. 160 | * 161 | * @covers \phpDocumentor\GraphViz\Attribute::__toString 162 | * @covers \phpDocumentor\GraphViz\Attribute::encodeSpecials 163 | */ 164 | public function testToStringWithSpecials(): void 165 | { 166 | $this->fixture = new Attribute('a', 'b'); 167 | 168 | $this->fixture->setValue('a\la'); 169 | $this->assertSame( 170 | 'a="a\la"', 171 | (string) $this->fixture, 172 | 'Specials should not be escaped' 173 | ); 174 | $this->fixture->setValue('a\l"a'); 175 | $this->assertSame( 176 | 'a="a\l\"a"', 177 | (string) $this->fixture, 178 | 'Specials should not be escaped, but quotes should' 179 | ); 180 | $this->fixture->setValue('a\\\\l"a'); 181 | $this->assertSame( 182 | 'a="a\\\\l\"a"', 183 | (string) $this->fixture, 184 | 'Double backslashes should stay the same' 185 | ); 186 | } 187 | 188 | /** 189 | * Tests whether the isValueContainingSpecials function 190 | * 191 | * @covers \phpDocumentor\GraphViz\Attribute::isValueContainingSpecials 192 | */ 193 | public function testIsValueContainingSpecials(): void 194 | { 195 | $this->fixture->setValue('+ name : string\l+ home_country : string\l'); 196 | $this->assertTrue($this->fixture->isValueContainingSpecials()); 197 | 198 | $this->fixture->setValue('+ ship(): boolean'); 199 | $this->assertFalse($this->fixture->isValueContainingSpecials()); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /tests/phpDocumentor/GraphViz/Test/EdgeTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 48 | Edge::class, 49 | $fixture 50 | ); 51 | $this->assertSame( 52 | $fromNode, 53 | $fixture->getFrom() 54 | ); 55 | $this->assertSame( 56 | $toNode, 57 | $fixture->getTo() 58 | ); 59 | } 60 | 61 | /** 62 | * Tests the create method 63 | * 64 | * @covers \phpDocumentor\GraphViz\Edge::create 65 | */ 66 | public function testCreate(): void 67 | { 68 | $this->assertInstanceOf( 69 | Edge::class, 70 | Edge::create(new Node('from'), new Node('to')) 71 | ); 72 | } 73 | 74 | /** 75 | * Tests whether the getFrom method returns the same node as passed 76 | * in the create method 77 | * 78 | * @covers \phpDocumentor\GraphViz\Edge::getFrom 79 | */ 80 | public function testGetFrom(): void 81 | { 82 | $from = new Node('from'); 83 | $edge = Edge::create($from, new Node('to')); 84 | $this->assertSame($from, $edge->getFrom()); 85 | } 86 | 87 | /** 88 | * Tests the getTo method returns the same node as passed 89 | * in the create method 90 | * 91 | * @covers \phpDocumentor\GraphViz\Edge::getTo 92 | */ 93 | public function testGetTo(): void 94 | { 95 | $to = new Node('to'); 96 | $edge = Edge::create(new Node('from'), $to); 97 | $this->assertSame($to, $edge->getTo()); 98 | } 99 | 100 | /** 101 | * Tests the magic __call method, to work as described, return the object 102 | * instance for a setX method, return the value for an getX method, and null 103 | * for the remaining method calls 104 | * 105 | * @covers \phpDocumentor\GraphViz\Edge::__call 106 | * @covers \phpDocumentor\GraphViz\Edge::setAttribute 107 | * @covers \phpDocumentor\GraphViz\Edge::getAttribute 108 | */ 109 | public function testCall(): void 110 | { 111 | $label = 'my label'; 112 | $fixture = new Edge(new Node('from'), new Node('to')); 113 | $this->assertInstanceOf(Edge::class, $fixture->setLabel($label)); 114 | $this->assertSame($label, $fixture->getLabel()->getValue()); 115 | $this->assertNull($fixture->someNonExcistingMethod()); 116 | } 117 | 118 | /** 119 | * @covers \phpDocumentor\GraphViz\Edge::getAttribute 120 | * @covers \phpDocumentor\GraphViz\AttributeNotFound::__construct 121 | */ 122 | public function testGetNonExistingAttributeThrowsAttributeNotFound(): void 123 | { 124 | $fixture = new Edge(new Node('from'), new Node('to')); 125 | 126 | $this->expectException(AttributeNotFound::class); 127 | $this->expectExceptionMessage('Attribute with name "label" was not found'); 128 | 129 | $fixture->getLabel(); 130 | } 131 | 132 | /** 133 | * Tests whether the magic __toString method returns a well formatted string 134 | * as specified in the DOT standard 135 | * 136 | * @covers \phpDocumentor\GraphViz\Edge::__toString 137 | */ 138 | public function testToString(): void 139 | { 140 | $fixture = new Edge(new Node('from'), new Node('to')); 141 | $fixture->setLabel('MyLabel'); 142 | $fixture->setWeight(45); 143 | 144 | $dot = << "to" [ 146 | label="MyLabel" 147 | weight="45" 148 | ] 149 | DOT; 150 | 151 | $this->assertSame($dot, (string) $fixture); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tests/phpDocumentor/GraphViz/Test/GraphTest.php: -------------------------------------------------------------------------------- 1 | fixture = new Graph(); 48 | } 49 | 50 | /** 51 | * Tears down the fixture, for example, closes a network connection. 52 | * This method is called after a test is executed. 53 | */ 54 | protected function tearDown(): void 55 | { 56 | m::close(); 57 | } 58 | 59 | /** 60 | * @covers \phpDocumentor\GraphViz\Graph::create 61 | */ 62 | public function testCreate(): void 63 | { 64 | $fixture = Graph::create(); 65 | $this->assertInstanceOf( 66 | Graph::class, 67 | $fixture 68 | ); 69 | $this->assertSame( 70 | 'G', 71 | $fixture->getName() 72 | ); 73 | $this->assertSame( 74 | 'digraph', 75 | $fixture->getType() 76 | ); 77 | 78 | $fixture = Graph::create('MyName', false); 79 | $this->assertSame( 80 | 'MyName', 81 | $fixture->getName() 82 | ); 83 | $this->assertSame( 84 | 'graph', 85 | $fixture->getType() 86 | ); 87 | } 88 | 89 | /** 90 | * @covers \phpDocumentor\GraphViz\Graph::setName 91 | */ 92 | public function testSetName(): void 93 | { 94 | $this->assertSame( 95 | $this->fixture, 96 | $this->fixture->setName('otherName'), 97 | 'Expecting a fluent interface' 98 | ); 99 | } 100 | 101 | /** 102 | * @covers \phpDocumentor\GraphViz\Graph::getName 103 | */ 104 | public function testGetName(): void 105 | { 106 | $this->assertSame( 107 | $this->fixture->getName(), 108 | 'G', 109 | 'Expecting the name to match the initial state' 110 | ); 111 | $this->fixture->setName('otherName'); 112 | $this->assertSame( 113 | $this->fixture->getName(), 114 | 'otherName', 115 | 'Expecting the name to contain the new value' 116 | ); 117 | } 118 | 119 | /** 120 | * @covers \phpDocumentor\GraphViz\Graph::setType 121 | */ 122 | public function testSetType(): void 123 | { 124 | $this->assertSame( 125 | $this->fixture, 126 | $this->fixture->setType('digraph'), 127 | 'Expecting a fluent interface' 128 | ); 129 | $this->assertSame( 130 | $this->fixture, 131 | $this->fixture->setType('graph'), 132 | 'Expecting a fluent interface' 133 | ); 134 | $this->assertSame( 135 | $this->fixture, 136 | $this->fixture->setType('subgraph'), 137 | 'Expecting a fluent interface' 138 | ); 139 | } 140 | 141 | /** 142 | * @covers \phpDocumentor\GraphViz\Graph::setType 143 | */ 144 | public function testSetTypeException(): void 145 | { 146 | $this->expectException(InvalidArgumentException::class); 147 | $this->fixture->setType('fakegraphg'); 148 | } 149 | 150 | /** 151 | * @covers \phpDocumentor\GraphViz\Graph::getType 152 | */ 153 | public function testGetType(): void 154 | { 155 | $this->assertSame( 156 | $this->fixture->getType(), 157 | 'digraph' 158 | ); 159 | $this->fixture->setType('graph'); 160 | $this->assertSame( 161 | $this->fixture->getType(), 162 | 'graph' 163 | ); 164 | } 165 | 166 | public function testSetStrict(): void 167 | { 168 | $this->assertSame( 169 | $this->fixture, 170 | $this->fixture->setStrict(true), 171 | 'Expecting a fluent interface' 172 | ); 173 | $this->assertSame( 174 | $this->fixture, 175 | $this->fixture->setStrict(false), 176 | 'Expecting a fluent interface' 177 | ); 178 | } 179 | 180 | public function testIsStrict(): void 181 | { 182 | $this->assertSame( 183 | $this->fixture->isStrict(), 184 | false 185 | ); 186 | $this->fixture->setStrict(true); 187 | $this->assertSame( 188 | $this->fixture->isStrict(), 189 | true 190 | ); 191 | } 192 | 193 | public function testSetPath(): void 194 | { 195 | $this->assertSame( 196 | $this->fixture, 197 | $this->fixture->setPath(__DIR__), 198 | 'Expecting a fluent interface' 199 | ); 200 | } 201 | 202 | /** 203 | * @covers \phpDocumentor\GraphViz\Graph::__call 204 | * @covers \phpDocumentor\GraphViz\Graph::getAttribute 205 | * @covers \phpDocumentor\GraphViz\Graph::setAttribute 206 | */ 207 | public function test__call(): void 208 | { 209 | $this->assertNull($this->fixture->MyMethod()); 210 | $this->assertSame($this->fixture, $this->fixture->setBgColor('black')); 211 | $this->assertSame('black', $this->fixture->getBgColor()->getValue()); 212 | } 213 | 214 | /** 215 | * @covers \phpDocumentor\GraphViz\Graph::getAttribute 216 | * @covers \phpDocumentor\GraphViz\AttributeNotFound::__construct 217 | */ 218 | public function testGetNonExistingAttributeThrowsAttributeNotFound(): void 219 | { 220 | $this->expectException(AttributeNotFound::class); 221 | $this->expectExceptionMessage('Attribute with name "notexisting" was not found'); 222 | 223 | $this->fixture->getNotExisting(); 224 | } 225 | 226 | /** 227 | * @covers \phpDocumentor\GraphViz\Graph::addGraph 228 | */ 229 | public function testAddGraph(): void 230 | { 231 | $mock = m::mock(Graph::class); 232 | $mock->expects('setType'); 233 | $mock->expects('getName'); 234 | 235 | $this->assertSame( 236 | $this->fixture, 237 | $this->fixture->addGraph($mock) 238 | ); 239 | } 240 | 241 | /** 242 | * @covers \phpDocumentor\GraphViz\Graph::hasGraph 243 | */ 244 | public function testHasGraph(): void 245 | { 246 | $mock = m::mock(Graph::class); 247 | $mock->expects('getName')->andReturn('MyName'); 248 | $mock->expects('setType'); 249 | 250 | $this->assertFalse($this->fixture->hasGraph('MyName')); 251 | $this->fixture->addGraph($mock); 252 | $this->assertTrue($this->fixture->hasGraph('MyName')); 253 | } 254 | 255 | /** 256 | * @covers \phpDocumentor\GraphViz\Graph::getGraph 257 | */ 258 | public function testGetGraph(): void 259 | { 260 | $mock = m::mock(Graph::class); 261 | $mock->expects('setType'); 262 | $mock->expects('getName')->andReturn('MyName'); 263 | 264 | $this->fixture->addGraph($mock); 265 | $this->assertSame( 266 | $mock, 267 | $this->fixture->getGraph('MyName') 268 | ); 269 | } 270 | 271 | /** 272 | * @covers \phpDocumentor\GraphViz\Graph::setNode 273 | */ 274 | public function testSetNode(): void 275 | { 276 | $mock = m::mock(Node::class); 277 | $mock->expects('getName')->andReturn('MyName'); 278 | 279 | $this->assertSame( 280 | $this->fixture, 281 | $this->fixture->setNode($mock) 282 | ); 283 | } 284 | 285 | /** 286 | * @covers \phpDocumentor\GraphViz\Graph::findNode 287 | */ 288 | public function testFindNode(): void 289 | { 290 | $this->assertNull($this->fixture->findNode('MyNode')); 291 | 292 | $mock = m::mock(Node::class); 293 | $mock->expects('getName')->andReturn('MyName'); 294 | 295 | $this->fixture->setNode($mock); 296 | $this->assertSame( 297 | $mock, 298 | $this->fixture->findNode('MyName') 299 | ); 300 | 301 | $subGraph = Graph::create(); 302 | $mock2 = m::mock(Node::class); 303 | $mock2->expects('getName')->andReturn('MyName2'); 304 | 305 | $subGraph->setNode($mock2); 306 | 307 | $this->fixture->addGraph($subGraph); 308 | $this->assertSame( 309 | $mock2, 310 | $this->fixture->findNode('MyName2') 311 | ); 312 | } 313 | 314 | /** 315 | * @covers \phpDocumentor\GraphViz\Graph::__set 316 | */ 317 | public function test__set(): void 318 | { 319 | $mock = m::mock(Node::class); 320 | 321 | $this->fixture->__set('myNode', $mock); 322 | 323 | self::assertSame($mock, $this->fixture->myNode); 324 | } 325 | 326 | /** 327 | * @covers \phpDocumentor\GraphViz\Graph::__get 328 | */ 329 | public function test__get(): void 330 | { 331 | $mock = m::mock(Node::class); 332 | 333 | $this->fixture->myNode = $mock; 334 | $this->assertSame( 335 | $mock, 336 | $this->fixture->myNode 337 | ); 338 | } 339 | 340 | /** 341 | * @covers \phpDocumentor\GraphViz\Graph::link 342 | */ 343 | public function testLink(): void 344 | { 345 | $mock = m::mock(Edge::class); 346 | 347 | $this->assertSame( 348 | $this->fixture, 349 | $this->fixture->link($mock) 350 | ); 351 | } 352 | 353 | /** 354 | * @covers \phpDocumentor\GraphViz\Graph::export 355 | */ 356 | public function testExportException(): void 357 | { 358 | $graph = Graph::create('My First Graph'); 359 | $filename = tempnam(sys_get_temp_dir(), 'tst'); 360 | 361 | if ($filename === false) { 362 | $this->assertFalse('Failed to create destination file'); 363 | 364 | return; 365 | } 366 | 367 | $this->expectException(Exception::class); 368 | $graph->export('fpd', $filename); 369 | } 370 | 371 | /** 372 | * @covers \phpDocumentor\GraphViz\Graph::export 373 | */ 374 | public function testExport(): void 375 | { 376 | $graph = Graph::create('My First Graph'); 377 | $filename = tempnam(sys_get_temp_dir(), 'tst'); 378 | 379 | if ($filename === false) { 380 | $this->assertFalse('Failed to create destination file'); 381 | 382 | return; 383 | } 384 | 385 | $this->assertSame( 386 | $graph, 387 | $graph->export('pdf', $filename) 388 | ); 389 | $this->assertTrue(is_readable($filename)); 390 | } 391 | 392 | /** 393 | * @covers \phpDocumentor\GraphViz\Graph::__toString 394 | */ 395 | public function test__toString(): void 396 | { 397 | $graph = Graph::create('My First Graph'); 398 | $this->assertSame( 399 | $this->normalizeLineEndings((string) $graph), 400 | $this->normalizeLineEndings(('digraph "My First Graph" {' . PHP_EOL . PHP_EOL . '}')) 401 | ); 402 | 403 | $graph->setLabel('PigeonPost'); 404 | $this->assertSame( 405 | $this->normalizeLineEndings((string) $graph), 406 | $this->normalizeLineEndings(('digraph "My First Graph" {' . PHP_EOL . 'label="PigeonPost"' . PHP_EOL . '}')) 407 | ); 408 | 409 | $graph->setStrict(true); 410 | $this->assertSame( 411 | $this->normalizeLineEndings((string) $graph), 412 | $this->normalizeLineEndings( 413 | ('strict digraph "My First Graph" {' . PHP_EOL . 'label="PigeonPost"' . PHP_EOL . '}') 414 | ) 415 | ); 416 | } 417 | 418 | /** 419 | * Help avoid issue of "#Warning: Strings contain different line endings!" on Windows. 420 | */ 421 | private function normalizeLineEndings(string $string): string 422 | { 423 | $result = preg_replace('~\R~u', "\r\n", $string); 424 | if ($result === null) { 425 | throw new RuntimeException('Normalize line endings failed'); 426 | } 427 | 428 | return $result; 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /tests/phpDocumentor/GraphViz/Test/NodeTest.php: -------------------------------------------------------------------------------- 1 | fixture = new Node('name', 'label'); 34 | } 35 | 36 | /** 37 | * Tests the construct method 38 | * 39 | * @covers \phpDocumentor\GraphViz\Node::__construct 40 | * @returnn void 41 | */ 42 | public function testConstruct(): void 43 | { 44 | $fixture = new Node('MyName', 'MyLabel'); 45 | $this->assertInstanceOf( 46 | Node::class, 47 | $fixture 48 | ); 49 | $this->assertSame('MyName', $fixture->getName()); 50 | $this->assertSame('MyLabel', $fixture->getLabel()->getValue()); 51 | } 52 | 53 | /** 54 | * Tests the create method 55 | * 56 | * @covers \phpDocumentor\GraphViz\Node::create 57 | * @returnn void 58 | */ 59 | public function testCreate(): void 60 | { 61 | $this->assertInstanceOf( 62 | Node::class, 63 | Node::create('name', 'label') 64 | ); 65 | } 66 | 67 | /** 68 | * Tests the getting and setting of the name. 69 | * 70 | * @covers \phpDocumentor\GraphViz\Node::getName 71 | * @covers \phpDocumentor\GraphViz\Node::setName 72 | */ 73 | public function testName(): void 74 | { 75 | $this->assertSame( 76 | $this->fixture->getName(), 77 | 'name', 78 | 'Expecting the name to match the initial state' 79 | ); 80 | $this->assertSame( 81 | $this->fixture, 82 | $this->fixture->setName('otherName'), 83 | 'Expecting a fluent interface' 84 | ); 85 | $this->assertSame( 86 | $this->fixture->getName(), 87 | 'otherName', 88 | 'Expecting the name to contain the new value' 89 | ); 90 | } 91 | 92 | /** 93 | * Tests the magic __call method, to work as described, return the object 94 | * instance for a setX method, return the value for an getX method, and null 95 | * for the remaining method calls 96 | * 97 | * @covers \phpDocumentor\GraphViz\Node::__call 98 | * @covers \phpDocumentor\GraphViz\Node::getAttribute 99 | * @covers \phpDocumentor\GraphViz\Node::setAttribute 100 | */ 101 | public function testCall(): void 102 | { 103 | $fontname = 'Bitstream Vera Sans'; 104 | $this->assertInstanceOf(Node::class, $this->fixture->setfontname($fontname)); 105 | $this->assertSame($fontname, $this->fixture->getfontname()->getValue()); 106 | $this->assertNull($this->fixture->someNonExistingMethod()); 107 | } 108 | 109 | /** 110 | * @covers \phpDocumentor\GraphViz\Node::getAttribute 111 | * @covers \phpDocumentor\GraphViz\AttributeNotFound::__construct 112 | */ 113 | public function testGetNonExistingAttributeThrowsAttributeNotFound(): void 114 | { 115 | $this->expectException(AttributeNotFound::class); 116 | $this->expectExceptionMessage('Attribute with name "fontname" was not found'); 117 | 118 | $this->fixture->getFontname(); 119 | } 120 | 121 | /** 122 | * Tests whether the magic __toString method returns a well formatted string 123 | * as specified in the DOT standard 124 | * 125 | * @covers \phpDocumentor\GraphViz\Node::__toString 126 | */ 127 | public function testToString(): void 128 | { 129 | $this->fixture->setfontsize(12); 130 | $this->fixture->setfontname('Bitstream Vera Sans'); 131 | 132 | $dot = <<assertSame($dot, (string) $this->fixture); 141 | } 142 | 143 | /** 144 | * Tests whether the magic __toString method returns a well formatted string 145 | * as specified in the DOT standard when the label contains slashes. 146 | * 147 | * @covers \phpDocumentor\GraphViz\Node::__toString 148 | */ 149 | public function testToStringWithLabelContainingSlashes(): void 150 | { 151 | $this->fixture->setfontsize(12); 152 | $this->fixture->setfontname('Bitstream Vera Sans'); 153 | $this->fixture->setLabel('\phpDocumentor\Descriptor\ProjectDescriptor'); 154 | 155 | $dot = <<assertSame($dot, (string) $this->fixture); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /tests/phpDocumentor/PHPStan/MethodReflectionExtensionTest.php: -------------------------------------------------------------------------------- 1 | fixture = new MethodReflectionExtension(); 32 | } 33 | 34 | /** 35 | * @dataProvider existingMethodProvider 36 | */ 37 | public function testNodeHasMethodReturnsTrue(string $className, string $methodName): void 38 | { 39 | $classReflection = m::mock(ClassReflection::class); 40 | $classReflection->shouldReceive('getName')->andReturn($className); 41 | 42 | $this->assertTrue($this->fixture->hasMethod($classReflection, $methodName)); 43 | } 44 | 45 | /** 46 | * @return array> 47 | */ 48 | public function existingMethodProvider(): array 49 | { 50 | return [ 51 | 'node::getLabel' => [ 52 | 'className' => Node::class, 53 | 'methodName' => 'getLabel', 54 | ], 55 | 'node::setLabel' => [ 56 | 'className' => Node::class, 57 | 'methodName' => 'setLabel', 58 | ], 59 | 'graph::setFontSize' => [ 60 | 'className' => Graph::class, 61 | 'methodName' => 'setFontSize', 62 | ], 63 | 'graph::getFontSize' => [ 64 | 'className' => Graph::class, 65 | 'methodName' => 'getFontSize', 66 | ], 67 | ]; 68 | } 69 | 70 | public function testAttributeType(): void 71 | { 72 | $classReflection = m::mock(ClassReflection::class); 73 | $classReflection->shouldReceive('getName')->andReturn(Node::class); 74 | 75 | $method = $this->fixture->getMethod($classReflection, 'setFontSize'); 76 | 77 | $this->assertInstanceOf(FloatType::class, $method->getVariants()[0]->getParameters()[0]->getType()); 78 | } 79 | 80 | public function testAttributeTypeOfNoneExisting(): void 81 | { 82 | $classReflection = m::mock(ClassReflection::class); 83 | $classReflection->shouldReceive('getName')->andReturn(Node::class); 84 | 85 | $method = $this->fixture->getMethod($classReflection, 'setColor'); 86 | 87 | $this->assertInstanceOf(StringType::class, $method->getVariants()[0]->getParameters()[0]->getType()); 88 | } 89 | } 90 | --------------------------------------------------------------------------------