diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 38013d0..a356eef 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,175 +1,52 @@ -on: - push: - branches: - - master - pull_request: -name: Qa workflow -env: - extensions: mbstring, intl, iconv, libxml, dom, json, simplexml, zlib, fileinfo - key: cache-v1 # can be any string, change to clear the extension cache. - defaultPHPVersion: '7.3' -jobs: - phpunit-with-coverage: - runs-on: ubuntu-latest - name: Unit tests - steps: - - uses: actions/checkout@v2 - - - name: Install graphviz - run: sudo apt-get update && sudo apt-get install -y graphviz - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ env.defaultPHPVersion }} - ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 - tools: phive - - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: composer-${{ hashFiles('**/composer.lock') }} - restore-keys: composer- - - - name: Install Composer dependencies - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Run PHPUnit - run: php vendor/bin/phpunit - - phpunit: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: - - ubuntu-latest - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] - name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }} - needs: - - phpunit-with-coverage - steps: - - uses: actions/checkout@v2 - - - name: Install graphviz - run: sudo apt-get update && sudo apt-get install -y graphviz - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extension-csv: mbstring, simplexml - ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 - - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: composer-${{ hashFiles('**/composer.lock') }} - restore-keys: composer- +# https://docs.github.com/en/actions - - name: Install Composer dependencies - run: | - composer install --no-progress --prefer-dist --optimize-autoloader +name: "Integrate" - - name: Run PHPUnit - run: php vendor/bin/phpunit - - codestyle: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Restore/cache vendor folder - uses: actions/cache@v1 - with: - path: vendor - key: all-build-${{ hashFiles('**/composer.lock') }} - restore-keys: | - all-build-${{ hashFiles('**/composer.lock') }} - all-build- - - name: Restore/cache tools folder - uses: actions/cache@v1 - with: - path: tools - key: all-tools-${{ github.sha }} - restore-keys: | - all-tools-${{ github.sha }}- - all-tools- - - name: Code style check - uses: docker://phpdoc/phpcs-ga:latest - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - args: -d memory_limit=1024M - - phpstan: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ env.defaultPHPVersion }} - ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 - tools: pecl - - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: composer-${{ hashFiles('**/composer.lock') }} - restore-keys: composer- - - - name: Install Composer dependencies - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: PHPStan - uses: phpDocumentor/phpstan-ga@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - args: analyse src tests --configuration phpstan.neon - - psalm: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 7.3 - ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 - tools: pecl, psalm - - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: composer-${{ hashFiles('**/composer.lock') }} - restore-keys: composer- - - - name: Install Composer dependencies - run: | - composer install --no-progress --prefer-dist --optimize-autoloader +on: # yamllint disable-line rule:truthy + push: + branches: + - "master" + pull_request: null + schedule: + - cron: "0 14 * * 1" + # Allow manually triggering the workflow. + workflow_dispatch: true - - name: Psalm - run: vendor/bin/psalm.phar --output-format=github +jobs: + code-coverage: + name: "Code Coverage" + uses: "phpDocumentor/.github/.github/workflows/code-coverage.yml@main" + with: + composer-root-version: "1.x-dev" + + coding-standards: + name: "Coding Standards" + uses: "phpDocumentor/.github/.github/workflows/coding-standards.yml@v0.8" + with: + composer-root-version: "1.x-dev" + + dependency-analysis: + name: "Dependency analysis" + uses: "phpDocumentor/.github/.github/workflows/dependency-analysis.yml@v0.8" + with: + composer-root-version: "1.x-dev" + + lint-root: + name: "Lint root" + uses: "phpDocumentor/.github/.github/workflows/lint.yml@main" + with: + composer-options: "--no-check-publish --ansi" + + static-analysis: + name: "Static analysis" + uses: "phpDocumentor/.github/.github/workflows/static-analysis.yml@v0.8" + with: + php-extensions: "none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, pcntl, posix" + composer-root-version: "1.x-dev" + + unit-tests: + name: "Unit test" + uses: "phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.8" + with: + composer-root-version: "1.x-dev" + upcoming-releases: true \ No newline at end of file diff --git a/Makefile b/Makefile index 0cd7a08..172d1ab 100644 --- a/Makefile +++ b/Makefile @@ -14,21 +14,26 @@ setup: install-phive phpcbf: docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest phpcbf ${ARGS} + +.PHONY: build-test-image +build-test-image: + docker build -t php-graphviz -f tests/Resources/Dockerfile tests/Resources + .PHONY: phpcs -phpcs: - docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest -d memory_limit=1024M -s +phpcs: build-test-image + docker run -it --rm -v${CURDIR}:/data -w /data php-graphviz vendor/bin/phpcs .PHONY: phpstan -phpstan: - docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project phpdoc/phpstan-ga:latest analyse src tests --configuration phpstan.neon ${ARGS} +phpstan: build-test-image + docker run -it --rm -v${CURDIR}:/data -w /data php-graphviz ./vendor/phpstan/phpstan/phpstan analyse src ${ARGS} .PHONY: psalm -psalm: - docker run -it --rm -v${CURDIR}:/data -w /data php:7.3 vendor/bin/psalm.phar +psalm: build-test-image + docker run -it --rm -v${CURDIR}:/data -w /data php-graphviz vendor/bin/psalm.phar .PHONY: test -test: - docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.3 vendor/bin/phpunit +test: build-test-image + docker run -it --rm -v${PWD}:/opt/project -w /opt/project php-graphviz ./vendor/bin/phpunit .PHONY: pre-commit-test pre-commit-test: phpcs phpstan psalm test diff --git a/README.md b/README.md index 57d9ab6..3e3f898 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,3 @@ GraphViz GraphViz is a library meant for generating .dot files for GraphViz with a fluent interface. - - -### PHPStan extension - -This library contains a number of magic methods to set attributes on `Node`, `Graph` and `Edge` -this will result in errors when using the library with checks by PHPStan. For your convenience this -library provides an phpStan extension so your code can be checked correctly by phpstan. - -``` -includes: - - vendor/phpdocumentor/graphviz/extension.neon -``` diff --git a/composer.json b/composer.json index 82fbbb8..72302f6 100644 --- a/composer.json +++ b/composer.json @@ -10,30 +10,35 @@ } ], "require": { - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "autoload": { "psr-4": { - "phpDocumentor\\GraphViz\\": "src/phpDocumentor/GraphViz", - "phpDocumentor\\GraphViz\\PHPStan\\": "./src/phpDocumentor/PHPStan" + "phpDocumentor\\GraphViz\\": "src/phpDocumentor/GraphViz" } }, "autoload-dev": { "psr-4": { - "phpDocumentor\\GraphViz\\Test\\": "./tests/phpDocumentor/GraphViz/Test", - "phpDocumentor\\GraphViz\\PHPStan\\": "./tests/phpDocumentor/PHPStan" + "phpDocumentor\\GraphViz\\Test\\": "./tests/phpDocumentor/GraphViz/Test" } }, "require-dev": { - "phpunit/phpunit": "^8.2 || ^9.2", - "mockery/mockery": "^1.2", - "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^11.0", + "mockery/mockery": "@stable", + "phpstan/phpstan": "@stable", "ext-simplexml": "*", - "psalm/phar": "^4.15 || ^5.0" + "psalm/phar": "@stable", + "squizlabs/php_codesniffer": "@stable", + "phpstan/phpstan-mockery": "@stable" }, "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.x-dev" + } + }, + "config": { + "allow-plugins": { + "ondrejmirtes/composer-attribute-collector": true } } } diff --git a/extension.neon b/extension.neon deleted file mode 100644 index 5f52740..0000000 --- a/extension.neon +++ /dev/null @@ -1,9 +0,0 @@ -services: - - - class: phpDocumentor\GraphViz\PHPStan\MethodReflectionExtension - tags: - - phpstan.broker.methodsClassReflectionExtension - - - class: phpDocumentor\GraphViz\PHPStan\GraphNodeReflectionExtension - tags: - - phpstan.broker.propertiesClassReflectionExtension diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 9d79b69..cf20d87 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -6,9 +6,6 @@ tests - - - tests/phpDocumentor/GraphViz/Test/GraphTest\.php diff --git a/phpstan.neon b/phpstan.neon index b9e02dd..4ee6ecc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,12 +1,8 @@ includes: - - /composer/vendor/phpstan/phpstan-mockery/extension.neon - - ./extension.neon + - ./vendor/phpstan/phpstan-mockery/extension.neon parameters: level: max ignoreErrors: #We have some runtime protection that needs to be tested. Ignore these errors - - '#Call to an undefined method phpDocumentor\\GraphViz\\Edge::someNonExcistingMethod\(\)\.#' - - '#Call to an undefined method phpDocumentor\\GraphViz\\Graph::MyMethod\(\)\.#' - - '#Call to an undefined method phpDocumentor\\GraphViz\\Graph::getNotExisting\(\)\.#' - - '#Call to an undefined method phpDocumentor\\GraphViz\\Node::someNonExistingMethod\(\)\.#' + - '#Cannot cast mixed to string#' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 98c74be..2650a22 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,9 +16,6 @@ ./tests/phpDocumentor/GraphViz - - ./tests/phpDocumentor/PHPStan - diff --git a/psalm.xml b/psalm.xml index 18636c6..d8c8cae 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,5 @@ + + diff --git a/src/phpDocumentor/GraphViz/Attribute.php b/src/phpDocumentor/GraphViz/Attribute.php index d55f35a..a6257e2 100644 --- a/src/phpDocumentor/GraphViz/Attribute.php +++ b/src/phpDocumentor/GraphViz/Attribute.php @@ -21,14 +21,16 @@ * Class representing a single GraphViz attribute. * * @link http://phpdoc.org + * + * @psalm-suppress ClassMustBeFinal */ class Attribute { - /** @var string The name of this attribute */ - protected $key = ''; + /** The name of this attribute */ + protected string $key = ''; - /** @var string The value of this attribute */ - protected $value = ''; + /** The value of this attribute */ + protected string $value = ''; /** * Creating a new attribute. diff --git a/src/phpDocumentor/GraphViz/AttributeNotFound.php b/src/phpDocumentor/GraphViz/AttributeNotFound.php index fe6b568..a6b782a 100644 --- a/src/phpDocumentor/GraphViz/AttributeNotFound.php +++ b/src/phpDocumentor/GraphViz/AttributeNotFound.php @@ -15,6 +15,9 @@ use function sprintf; +/** + * @psalm-suppress ClassMustBeFinal + */ class AttributeNotFound extends Exception { public function __construct(string $name) diff --git a/src/phpDocumentor/GraphViz/Attributes.php b/src/phpDocumentor/GraphViz/Attributes.php index 4b42896..005d1d0 100644 --- a/src/phpDocumentor/GraphViz/Attributes.php +++ b/src/phpDocumentor/GraphViz/Attributes.php @@ -18,11 +18,11 @@ trait Attributes { /** @var Attribute[] */ - protected $attributes = []; + protected array $attributes = []; - public function setAttribute(string $name, string $value): self + public function setAttribute(string $name, string|int|float|\Stringable $value): self { - $this->attributes[$name] = new Attribute($name, $value); + $this->attributes[$name] = new Attribute($name, (string) $value); return $this; } @@ -38,4 +38,41 @@ public function getAttribute(string $name): Attribute return $this->attributes[$name]; } + + /** + * Magic method to provide a getter/setter to add attributes on the Node. + * + * Using this method we make sure that we support any attribute without + * too much hassle. If the name for this method does not start with get or + * set we return null. + * + * Set methods return this graph (fluent interface) whilst get methods + * return the attribute value. + * + * @param string $name Method name; either getX or setX is expected. + * @param mixed[] $arguments List of arguments; only 1 is expected for setX. + * + * @throws AttributeNotFound + * + * @psalm-suppress PossiblyUnusedReturnValue + */ + public function __call(string $name, array $arguments): mixed + { + $key = $this->normalizeKey(substr($name, 3)); + $action = strtolower(substr($name, 0, 3)); + if ($action === 'set') { + return $this->setAttribute($key, (string) $arguments[0]); + } + + if ($action === 'get') { + return $this->getAttribute($key); + } + + return null; + } + + private function normalizeKey(string $key): string + { + return lcfirst($key); + } } diff --git a/src/phpDocumentor/GraphViz/Edge.php b/src/phpDocumentor/GraphViz/Edge.php index 0606087..82116dd 100644 --- a/src/phpDocumentor/GraphViz/Edge.php +++ b/src/phpDocumentor/GraphViz/Edge.php @@ -22,16 +22,18 @@ * Class representing an edge (arrow, line). * * @link http://phpdoc.org + * + * @psalm-suppress ClassMustBeFinal */ -class Edge +class Edge implements \Stringable { use Attributes; - /** @var Node Node from where to link */ - private $from; + /** Node from where to link */ + private Node $from; - /** @var Node Node where to to link */ - private $to; + /** Node where to link */ + private Node $to; /** * Creates a new Edge / Link between the given nodes. @@ -76,38 +78,6 @@ public function getTo(): Node return $this->to; } - /** - * Magic method to provide a getter/setter to add attributes on the edge. - * - * Using this method we make sure that we support any attribute without too - * much hassle. If the name for this method does not start with get or set - * we return null. - * - * Set methods return this graph (fluent interface) whilst get methods - * return the attribute value. - * - * @param string $name name of the invoked method, expect it to be - * setX or getX. - * @param mixed[] $arguments Arguments for the setter, only 1 is expected: value - * - * @return Attribute|Edge|null - * - * @throws AttributeNotFound - */ - public function __call(string $name, array $arguments) - { - $key = strtolower(substr($name, 3)); - if (strtolower(substr($name, 0, 3)) === 'set') { - return $this->setAttribute($key, (string) $arguments[0]); - } - - if (strtolower(substr($name, 0, 3)) === 'get') { - return $this->getAttribute($key); - } - - return null; - } - /** * Returns the edge definition as is requested by GraphViz. */ @@ -124,8 +94,8 @@ public function __toString(): string $toName = addslashes($this->getTo()->getName()); return << "${toName}" [ -${attributes} +"{$fromName}" -> "{$toName}" [ +{$attributes} ] DOT; } diff --git a/src/phpDocumentor/GraphViz/Graph.php b/src/phpDocumentor/GraphViz/Graph.php index f848d06..212be28 100644 --- a/src/phpDocumentor/GraphViz/Graph.php +++ b/src/phpDocumentor/GraphViz/Graph.php @@ -45,31 +45,33 @@ * @method Graph setRankDir(string $rankDir) * @method Graph setSplines(string $splines) * @method Graph setConcentrate(string $concentrate) + * + * @psalm-suppress ClassMustBeFinal */ -class Graph +class Graph implements \Stringable { use Attributes; - /** @var string Name of this graph */ - protected $name = 'G'; + /** Name of this graph */ + protected string $name = 'G'; - /** @var string Type of this graph; may be digraph, graph or subgraph */ - protected $type = 'digraph'; + /** Type of this graph; may be digraph, graph or subgraph */ + protected string $type = 'digraph'; - /** @var bool If the graph is strict then multiple edges are not allowed between the same pairs of nodes */ - protected $strict = false; + /** If the graph is strict then multiple edges are not allowed between the same pairs of nodes */ + protected bool $strict = false; /** @var Graph[] A list of subgraphs for this Graph */ - protected $graphs = []; + protected array $graphs = []; /** @var Node[] A list of nodes for this Graph */ - protected $nodes = []; + protected array $nodes = []; /** @var Edge[] A list of edges / arrows for this Graph */ - protected $edges = []; + protected array $edges = []; - /** @var string The path to execute dot from */ - protected $path = ''; + /** The path to execute dot from */ + protected string $path = ''; /** * Factory method to instantiate a Graph so that you can use fluent coding @@ -174,37 +176,6 @@ public function isStrict(): bool return $this->strict; } - /** - * Magic method to provide a getter/setter to add attributes on the Graph. - * - * Using this method we make sure that we support any attribute without - * too much hassle. If the name for this method does not start with get - * or set we return null. - * - * Set methods return this graph (fluent interface) whilst get methods - * return the attribute value. - * - * @param string $name Name of the method including get/set - * @param mixed[] $arguments The arguments, should be 1: the value - * - * @return Attribute|Graph|null - * - * @throws AttributeNotFound - */ - public function __call(string $name, array $arguments) - { - $key = strtolower(substr($name, 3)); - if (strtolower(substr($name, 0, 3)) === 'set') { - return $this->setAttribute($key, (string) $arguments[0]); - } - - if (strtolower(substr($name, 0, 3)) === 'get') { - return $this->getAttribute($key); - } - - return null; - } - /** * Adds a subgraph to this graph; automatically changes the type to subgraph. * @@ -262,6 +233,15 @@ public function setNode(Node $node): self return $this; } + public function getNode(string $name): Node + { + if (!isset($this->nodes[$name])) { + throw new \LogicException("Node with name '$name' does not exist in this graph."); + } + + return $this->nodes[$name]; + } + /** * Finds a node in this graph or any of its subgraphs. * @@ -351,7 +331,7 @@ public function export(string $type, string $filename): self // create the dot output $output = []; $code = 0; - exec($this->path . "dot -T${type} -o${filename} < ${tmpfileArg} 2>&1", $output, $code); + exec($this->path . "dot -T{$type} -o{$filename} < {$tmpfileArg} 2>&1", $output, $code); unlink($tmpfile); if ($code !== 0) { @@ -390,7 +370,7 @@ public function __toString(): string return <<getType()} "{$this->getName()}" { -${attributes} +{$attributes} } DOT; } diff --git a/src/phpDocumentor/GraphViz/Node.php b/src/phpDocumentor/GraphViz/Node.php index 4956802..5a446d1 100644 --- a/src/phpDocumentor/GraphViz/Node.php +++ b/src/phpDocumentor/GraphViz/Node.php @@ -24,13 +24,15 @@ * @link http://phpdoc.org * * @method void setLabel(string $name) Sets the label for this node. + * + * @psalm-suppress ClassMustBeFinal */ -class Node +class Node implements \Stringable { use Attributes; - /** @var string Name for this node */ - protected $name = ''; + /** Name for this node */ + protected string $name = ''; /** * Creates a new node with name and optional label. @@ -83,37 +85,6 @@ public function getName(): string return $this->name; } - /** - * Magic method to provide a getter/setter to add attributes on the Node. - * - * Using this method we make sure that we support any attribute without - * too much hassle. If the name for this method does not start with get or - * set we return null. - * - * Set methods return this graph (fluent interface) whilst get methods - * return the attribute value. - * - * @param string $name Method name; either getX or setX is expected. - * @param mixed[] $arguments List of arguments; only 1 is expected for setX. - * - * @return Attribute|Node|null - * - * @throws AttributeNotFound - */ - public function __call(string $name, array $arguments) - { - $key = strtolower(substr($name, 3)); - if (strtolower(substr($name, 0, 3)) === 'set') { - return $this->setAttribute($key, (string) $arguments[0]); - } - - if (strtolower(substr($name, 0, 3)) === 'get') { - return $this->getAttribute($key); - } - - return null; - } - /** * Returns the node definition as is requested by GraphViz. */ @@ -130,7 +101,7 @@ public function __toString(): string return <<classReflection = $classReflection; - $this->name = $name; - } - - public function getDeclaringClass(): ClassReflection - { - return $this->classReflection; - } - - public function isStatic(): bool - { - return false; - } - - public function isPrivate(): bool - { - return false; - } - - public function isPublic(): bool - { - return true; - } - - public function getName(): string - { - return $this->name; - } - - public function getPrototype(): ClassMemberReflection - { - return $this; - } - - /** - * @return ParametersAcceptor[] - */ - public function getVariants(): array - { - return [ - new FunctionVariant( - TemplateTypeMap::createEmpty(), - null, - [], - false, - new ObjectType(Attribute::class) - ), - ]; - } - - public function getDocComment(): ?string - { - return null; - } - - public function isDeprecated(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getDeprecatedDescription(): ?string - { - return null; - } - - public function isFinal(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function isInternal(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getThrowType(): ?Type - { - return new ObjectType(AttributeNotFound::class); - } - - public function hasSideEffects(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } -} diff --git a/src/phpDocumentor/PHPStan/AttributeSetterMethodReflection.php b/src/phpDocumentor/PHPStan/AttributeSetterMethodReflection.php deleted file mode 100644 index 961150a..0000000 --- a/src/phpDocumentor/PHPStan/AttributeSetterMethodReflection.php +++ /dev/null @@ -1,127 +0,0 @@ -classReflection = $classReflection; - $this->name = $name; - $this->attributeType = $attributeType; - } - - public function getDeclaringClass(): ClassReflection - { - return $this->classReflection; - } - - public function isStatic(): bool - { - return false; - } - - public function isPrivate(): bool - { - return false; - } - - public function isPublic(): bool - { - return true; - } - - public function getName(): string - { - return $this->name; - } - - public function getPrototype(): ClassMemberReflection - { - return $this; - } - - /** - * @return ParametersAcceptor[] - */ - public function getVariants(): array - { - return [ - new FunctionVariant( - TemplateTypeMap::createEmpty(), - TemplateTypeMap::createEmpty(), - [ - new DummyParameter('value', $this->attributeType, false, null, false, null), - ], - false, - new ObjectType($this->classReflection->getName()) - ), - ]; - } - - public function getDocComment(): ?string - { - return null; - } - - public function isDeprecated(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getDeprecatedDescription(): ?string - { - return null; - } - - public function isFinal(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function isInternal(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getThrowType(): ?Type - { - return new ObjectType(AttributeNotFound::class); - } - - public function hasSideEffects(): TrinaryLogic - { - return TrinaryLogic::createYes(); - } -} diff --git a/src/phpDocumentor/PHPStan/GraphNodeReflectionExtension.php b/src/phpDocumentor/PHPStan/GraphNodeReflectionExtension.php deleted file mode 100644 index b6793e4..0000000 --- a/src/phpDocumentor/PHPStan/GraphNodeReflectionExtension.php +++ /dev/null @@ -1,40 +0,0 @@ -getName() === Graph::class; - } - - public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection - { - return new AnnotationPropertyReflection( - $classReflection, - new ObjectType(Node::class), - true, - true - ); - } -} diff --git a/src/phpDocumentor/PHPStan/MethodReflectionExtension.php b/src/phpDocumentor/PHPStan/MethodReflectionExtension.php deleted file mode 100644 index 951b3b8..0000000 --- a/src/phpDocumentor/PHPStan/MethodReflectionExtension.php +++ /dev/null @@ -1,143 +0,0 @@ - 'node', - Graph::class => 'graph', - Edge::class => 'edge', - ]; - - public function hasMethod(ClassReflection $classReflection, string $methodName): bool - { - if (!array_key_exists($classReflection->getName(), self::SUPPORTED_CLASSES)) { - return false; - } - - $methods = $this->getMethodsFromSpec(self::SUPPORTED_CLASSES[$classReflection->getName()]); - $expectedAttribute = $this->getAttributeFromMethodName($methodName); - - return in_array($expectedAttribute, $methods, true); - } - - public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection - { - if (stripos($methodName, 'get') === 0) { - return new AttributeGetterMethodReflection($classReflection, $methodName); - } - - $attributeName = $this->getAttributeFromMethodName($methodName); - - return new AttributeSetterMethodReflection( - $classReflection, - $methodName, - $this->getAttributeInputType($attributeName) - ); - } - - /** - * @return string[] - */ - private function getMethodsFromSpec(string $className): array - { - $simpleXml = $this->getAttributesXmlDoc(); - - $elements = $simpleXml->xpath(sprintf("xsd:complexType[@name='%s']/xsd:attribute", $className)); - - if ($elements === false) { - throw new InvalidArgumentException( - sprintf('Class "%s" does not exist in Graphviz spec', $className) - ); - } - - return array_map( - static function (SimpleXMLElement $attribute): string { - return strtolower((string) $attribute['ref']); - }, - $elements - ); - } - - private function getAttributeInputType(string $ref): Type - { - $simpleXml = $this->getAttributesXmlDoc(); - $attributes = $simpleXml->xpath(sprintf("xsd:attribute[@name='%s']", $ref)); - - if (empty($attributes)) { - return new StringType(); - } - - $type = $attributes[0]['type']; - $type = str_replace('xsd:', '', (string) $type); - switch ($type) { - case 'boolean': - return new BooleanType(); - - case 'decimal': - return new FloatType(); - - case 'string': - default: - return new StringType(); - } - } - - private function getAttributesXmlDoc(): SimpleXMLElement - { - $fileContent = file_get_contents(__DIR__ . '/assets/attributes.xml'); - - if ($fileContent === false) { - throw new RuntimeException('Cannot read attributes spec'); - } - - $xml = simplexml_load_string($fileContent); - if ($xml === false) { - throw new RuntimeException('Cannot read attributes spec'); - } - - return $xml; - } - - private function getAttributeFromMethodName(string $methodName): string - { - return strtolower(substr($methodName, 3)); - } -} diff --git a/src/phpDocumentor/PHPStan/assets/attributes.xml b/src/phpDocumentor/PHPStan/assets/attributes.xml deleted file mode 100644 index 0d63e45..0000000 --- a/src/phpDocumentor/PHPStan/assets/attributes.xml +++ /dev/null @@ -1,2656 +0,0 @@ - - - - - - - - - - - All Graphviz attributes are specified by name-value pairs. Thus, to - set the fillcolor of a node abc, one would use - - - abc [fillcolor = red] - - - Similarly, to set the arrowhead style of an edge abc -> def, - one would use - - - abc -> def [arrowhead = diamond] - - - Further details concerning the setting of attributes can be found - in the description of the - DOT language. - - - - - - - At present, most device-independent units are either inches or - points, - which we take as 72 points per inch. - - - - - - - - - Some attributes, such as - dir or arrowtail, are - ambiguous when used in - DOT - with an undirected graph since the head and tail of an edge are meaningless. - As a convention, the first time an undirected edge appears, the - DOT - parser will assign the left node as the tail node and the right node as - the head. For example, the edge A -- B will have tail A - and head B. It is the user's responsibility to handle such - edges consistently. If the edge appears later, in the format - - - B -- A [taillabel = "tail"] - - - the drawing will attach the tail label to node A. - To avoid possible confusion when such attributes are required, the user - is encouraged to use a directed graph. - If it is important to make the graph appear undirected, this can be - done using the dir, arrowtail - or arrowhead attributes. - - - - - - - - - - - - - - - - - - - - - - - - - - - For undirected edges T -- H;, one of the nodes, usually - the righthand one, is treated as the head for the purpose of - interpreting forward and back. - - - - - - - - - - - - - - - - - String allowing escape sequences which are replaced according - to the context. - For node attributes, the substring \N is replaced by the name of the node, - and the substring \G by the name of the graph. - For graph or cluster attributes, the substring \G is replaced by the - name of the graph or cluster. - For edge attributes, the substring \E is replaced by the name of the edge, - the substring \G is replaced by the name of the graph or cluster, - and the substrings \T and \H by the names of - the tail and head nodes, respectively. - The name of an edge is the string formed from the name of the - tail node, the appropriate edge operator (-- or ->) and the name of the - head node. - - - In addition, if the associated attribute is - label, - headlabel or taillabel, - the escape sequences \n, \l and \r - divide the label into lines, centered, left-justified, and right-justified, - respectively. - - - - - - - - - - - - These specify the order in which nodes and edges are drawn in concrete - output. The default breadthfirst is the simplest, but when the graph - layout does not avoid edge-node overlap, this mode will sometimes have - edges drawn over nodes and sometimes on top of nodes. If the mode - nodesfirst is chosen, all nodes are drawn first, followed by the - edges. This guarantees an edge-node overlap will not be mistaken for - an edge ending at a node. On the other hand, usually for aesthetic - reasons, it may be desirable that all edges appear beneath nodes, even - if the resulting drawing is ambiguous. This can be achieved by choosing - edgesfirst. - - - - - - - - - - - - - - - These specify the granularity of packing connected components when - the pack attribute is true. A value of node causes - packing at the node and edge label, with no overlapping of these objects. - This produces a layout with the least area, but it also allows interleaving, - where a node of one component may lie between two nodes in another - component. A value of graph does a packing using the bounding box of the - component. Thus, there will be a rectangular region around a component - free of elements of any other component. - A value of clust guarantees that top-level clusters are kept intact. - What effect a value has also depends on the layout algorithm. For - example, neato does not support clusters, so a value of clust will - have the same effect as the default node value. - - - - - - - - - - - - - - - These specify the 8 row or column major orders for traversing a - rectangular array, the first character corresponding to the major - order and the second to the minor order. Thus, for "BL", the - major order is from bottom to top, and the minor order is from left - to right. This means the bottom row is traversed first, from left - to right, then the next row up, from left to right, and so on, - until the topmost row is traversed. - - - - - - - - - - - - - - - - - - - - - - List of pointf, separated by spaces. - - - - - - - - - - - - - - - - - - - - - - Corresponding to directed graphs drawn - from top to bottom, from left to right, from bottom to top, and from - right to left, respectively. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Specifies the path to images referenced within the graph. - - - - - - - - - - Factor damping force motions. On each iteration, a nodes movement - is limited to this factor of its potential motion. By being less than - 1.0, the system tends to "cool", thereby preventing cycling. - - - - - - - - - - Spring constant used in virtual physical model. It roughly corresponds - to an ideal edge length (in inches), in that increasing K tends to - increase the distance between nodes. - Note that the edge attribute len can be used to - override this value for adjacent nodes. - - - - - - - - - - Hyperlinks incorporated into device-dependent output. - At present, used in ps2, cmap, i*map and svg formats. - For all these formats, URLs can be attached to nodes, edges and - clusters. URL attributes can also be attached to the root graph in ps2, - cmap and i*map formats. This serves as the base URL for relative URLs in the - former, and as the default image map file in the latter. - - - For svg, cmapx and imap output, the active area for a node is its - visible image. - For example, an unfilled node with no drawn boundary will only be active on its label. - For other output, the active area is its bounding box. - The active area for a cluster is its bounding box. - For edges, the active areas are small circles where the edge contacts its head - and tail nodes. In addition, for svg, cmapx and imap, the active area - includes a thin polygon approximating the edge. The circles may - overlap the related node, and the edge URL dominates. - If the edge has a label, this will also be active. - Finally, if the edge has a head or tail label, this will also be active. - - - Note that, for edges, the attributes headURL, - tailURL, labelURL and - edgeURL allow control of various parts of an - edge. Also note that, if active areas of two edges overlap, it is unspecified - which area dominates. - - - - - - - - - - Style of arrowhead on the head node of an edge. - See also the dir attribute, - and the undirected note. - - - - - - - - - - Multiplicative scale factor for arrowheads. - - - - - - - - - - Style of arrowhead on the tail node of an edge. - See also the dir attribute, - and the undirected note. - - - - - - - - - - Bounding box of drawing in integer points. - - - - - - - - - - When attached to the root graph, this color is used as the background for - entire canvas. When a cluster attribute, it is used as the initial - background for the cluster. If a cluster has a filled - style, the - cluster's fillcolor will overlay the - background color. - - - If no background color is specified for the root graph, no graphics - operation are performed on the background. This works fine for - PostScript but for bitmap output, all bits are initialized to something. - This means that when the bitmap output is included in some other - document, all of the bits within the bitmap's bounding box will be - set, overwriting whatever color or graphics where already on the page. - If this effect is not desired, and you only want to set bits explicitly - assigned in drawing the graph, set bgcolor="transparent". - - - - - - - - - - If true, the drawing is centered in the output canvas. - - - - - - - - - - Specifies the character encoding used when interpreting string input - as a text label. The default value is UTF-8. - The other legal value is iso-8859-1 or, - equivalently, - Latin1. The charset attribute is case-insensitive. - Note that if the character encoding used in the input does not - match the charset value, the resulting output may be very strange. - - - - - - - - - - Mode used for handling clusters. If clusterrank is local, a - subgraph whose name begins with "cluster" is given special treatment. - The subgraph is laid out separately, and then integrated as a unit into - its parent graph, with a bounding rectangle drawn about it. - If the cluster has a label parameter, this label - is displayed within the rectangle. - Note also that there can be clusters within clusters. - At present, the modes global and none - appear to be identical, both turning off the special cluster processing. - - - - - - - - - - Basic drawing color for graphics, not text. For the latter, use the - fontcolor attribute. - - - For edges, the value - can either be a single color or a colorList. - In the latter case, the edge is drawn using parallel splines or lines, - one for each color in the list, in the order given. - The head arrow, if any, is drawn using the first color in the list, - and the tail arrow, if any, the second color. This supports the common - case of drawing opposing edges, but using parallel splines instead of - separately routed multiedges. - - - - - - - - - - This attribute specifies a color scheme namespace. If defined, it specifies - the context for interpreting color names. In particular, if a - color value has form xxx or //xxx, - then the color xxx will be evaluated according to the current color scheme. - If no color scheme is set, the standard X11 naming is used. - For example, if colorscheme=bugn9, then color=7 - is interpreted as /bugn9/7. - - - - - - - - - - Comments are inserted into output. Device-dependent. - - - - - - - - - - If true, allow edges between clusters. (See lhead and ltail below.) - - - - - - - - - - If true, use edge concentrators. - - - - - - - - - - If false, the edge is not used in ranking the nodes. - - - - - - - - - - If true, attach edge label to edge by a 2-segment - polyline, underlining the label, then going to the closest point of spline. - - - - - - - - - - This specifies the distance between nodes in separate connected - components. If set too small, connected components may overlap. - Only applicable if pack=false. - - - - - - - - - - Set the number of dimensions used for the layout. The maximum value - allowed is 10. - - - - - - - - - - Set edge type for drawing arrowheads. This indicates which ends of the - edge should be decorated with an arrowhead. The actual style of the - arrowhead can be specified using the arrowhead - and arrowtail attributes. - See undirected. - - - - - - - - - - Only valid when mode="ipsep". - If true, constraints are generated for each edge in the largest (heuristic) - directed acyclic subgraph such that the edge must point downwards. - If hier, generates level constraints similar to those used with - mode="hier". The main difference is that, in the latter - case, only these constraints are involved, so a faster solver can be used. - - - - - - - - - - Distortion factor for shape=polygon. - Positive values cause top part to - be larger than bottom; negative values do the opposite. - - - - - - - - - - This specifies the expected number of pixels per inch on a display device. - For bitmap output, this guarantees that text rendering will be - done more accurately, both in size and in placement. For SVG output, - it is used to guarantee that the dimensions in the output correspond to - the correct number of points or inches. - - - - - - - - - - If edgeURL is defined, this is the link used for the non-label - parts of an edge. This value overrides any URL - defined for the edge. - Also, this value is used near the head or tail node unless overridden - by a headURL or tailURL value, - respectively. - See undirected. - - - - - - - - - - Synonym for edgeURL. - - - - - - - - - - If the edge has a URL or edgeURL - attribute, this attribute determines which window of the - browser is used - for the URL attached to the non-label part of the edge. - Setting it to "_graphviz" will open a new window if it - doesn't already exist, or reuse it if it does. - If undefined, the value of the target is used. - - - - - - - - - - Tooltip annotation attached to the non-label part of an edge. - This is used only if the edge has a URL - or edgeURL attribute. - - - - - - - - - - Terminating condition. If the length squared of all energy gradients are - < epsilon, the algorithm stops. - - - - - - - - - - Fraction to increase polygons (multiply - coordinates by 1 + esep) for purposes of spline edge routing. - This should normally be strictly less than - sep. - - - - - - - - - - Color used to fill the background of a node or cluster - assuming style=filled. - If fillcolor is not defined, color is - used. (For clusters, if color is not defined, - bgcolor is used.) If this is not defined, - the default is used, except for - shape=point or when the output - format is MIF, - which use black by default. - - - Note that a cluster inherits the root graph's attributes if defined. - Thus, if the root graph has defined a fillcolor, this will override a - color or bgcolor attribute set for the cluster. - - - - - - - - - - If true, the node size is specified by the values of the - width - and height attributes only - and is not expanded to contain the text label. - - - - - - - - - - Color used for text. - - - - - - - - - - Font used for text. This very much depends on the output format and, for - non-bitmap output such as PostScript or SVG, the availability of the font - when the graph is displayed or printed. As such, it is best to rely on - font faces that are generally available, such as Times-Roman, Helvetica or - Courier. - - - If Graphviz was built using the - fontconfig library, the latter library - will be used to search for the font. However, if the fontname string - contains a slash character "/", it is treated as a pathname for the font - file, though font lookup will append the usual font suffixes. - - - If Graphviz does not use fontconfig, fontname will be - considered the name of a Type 1 or True Type font file. - If you specify fontname=schlbk, the tool will look for a - file named schlbk.ttf or schlbk.pfa or schlbk.pfb - in one of the directories specified by - the fontpath attribute. - The lookup does support various aliases for the common fonts. - - - - - - - - - - Allows user control of how basic fontnames are represented in SVG output. - If fontnames is undefined or svg, - the output will try to use known SVG fontnames. For example, the - default font Times-Roman will be mapped to the - basic SVG font serif. This can be overridden by setting - fontnames to ps or gd. - In the former case, known PostScript font names such as - Times-Roman will be used in the output. - In the latter case, the fontconfig font conventions - are used. Thus, Times-Roman would be treated as - Nimbus Roman No9 L. These last two options are useful - with SVG viewers that support these richer fontname spaces. - - - - - - - - - - Directory list used by libgd to search for bitmap fonts if Graphviz - was not built with the fontconfig library. - If fontpath is not set, the environment - variable DOTFONTPATH is checked. - If that is not set, GDFONTPATH is checked. - If not set, libgd uses its compiled-in font path. - Note that fontpath is an attribute of the root graph. - - - - - - - - - - Font size, in points, used for text. - - - - - - - - - - If the end points of an edge belong to the same group, i.e., have the - same group attribute, parameters are set to avoid crossings and keep - the edges straight. - - - - - - - - - - If headURL is defined, it is - output as part of the head label of the edge. - Also, this value is used near the head node, overriding any - URL value. - See undirected. - - - - - - - - - - If true, the head of an edge is clipped to the boundary of the head node; - otherwise, the end of the edge goes to the center of the node, or the - center of a port, if applicable. - - - - - - - - - - Synonym for headURL. - - - - - - - - - - Text label to be placed near head of edge. - See undirected. - - - - - - - - - - Indicates where on the head node to attach the head of the edge. - In the default case, the edge is aimed towards the center of the node, - and then clipped at the node boundary. - See undirected. - - - - - - - - - - If the edge has a headURL, - this attribute determines which window of the - browser is used - for the URL. Setting it to "_graphviz" will open a new window if it - doesn't already exist, or reuse it if it does. - If undefined, the value of the target is used. - - - - - - - - - - Tooltip annotation attached to the head of an edge. This is used only - if the edge has a headURL attribute. - - - - - - - - - - Height of node, in inches. This is taken as the initial, minimum height - of the node. If fixedsize is true, this - will be the final height of the node. Otherwise, if the node label - requires more height to fit, the node's height will be increased to - contain the label. Note also that, if the output format is dot, the - value given to height will be the final value. - - - - - - - - - - Synonym for URL. - - - - - - - - - - Gives the name of a file containing an image to be displayed inside - a node. The image file must be in one of the recognized formats, - typically JPEG, PNG, GIF or Postscript, and be able to be converted - into the desired output format. - - - Unlike with the shapefile attribute, - the image is treated as node - content rather than the entire node. In particular, an image can - be contained in a node of any shape, not just a rectangle. - - - - - - - - - - Attribute controlling how an image fills its - containing node. In general, the image is given its natural size, - (cf. dpi), - and the node size is made large enough to contain its image, its - label, its margin, and its peripheries. - Its width and height will also be at least as large as its - minimum width and height. - If, however, fixedsize=true, - the width and height attributes specify the exact size of the node. - - - During rendering, in the default case (imagescale=false), - the image retains its natural size. - If true, - the image is uniformly scaled (i.e., its aspect ratio is - preserved) to fit inside the node. - At least one dimension of the image will be as large as possible - given the size of the node. - When width, - the width of the image is scaled to fill the node width. - The corresponding property holds when imagescale=height. - When both, - both the height and the width are scaled separately to fill the node. - - - In all cases, if a dimension of the image is larger than the - corresponding dimension of the node, that dimension of the - image is scaled down to fit the node. As with the case of - expansion, if imagescale=true, width and height are - scaled uniformly. - - - - - - - - - - Text label attached to objects. - If a node's shape is record, then the label can - have a special format - which describes the record layout. - - - - - - - - - - If labelURL is defined, this is the link used for the label - of an edge. This value overrides any URL - defined for the edge. - - - - - - - - - - This, along with labeldistance, determine - where the - headlabel (taillabel) are placed with respect to the head (tail) - in polar coordinates. The origin in the coordinate system is - the point where the edge touches the node. The ray of 0 degrees - goes from the origin back along the edge, parallel to the edge - at the origin. - - - The angle, in degrees, specifies the rotation from the 0 degree ray, - with positive angles moving counterclockwise and negative angles - moving clockwise. - - - - - - - - - - Multiplicative scaling factor adjusting the distance that - the headlabel (taillabel) is from the head (tail) node. - The default distance is 10 points. See labelangle - for more details. - - - - - - - - - - If true, allows edge labels to be less constrained in position. - In particular, it may appear on top of other edges. - - - - - - - - - - Color used for headlabel and taillabel. - If not set, defaults to edge's fontcolor. - - - - - - - - - - Font used for headlabel and taillabel. - If not set, defaults to edge's fontname. - - - - - - - - - - Font size, in points, used for headlabel and taillabel. - If not set, defaults to edge's fontsize. - - - - - - - - - - Synonym for labelURL. - - - - - - - - - - Justification for cluster labels. If r, the label - is right-justified within bounding rectangle; if l, left-justified; - else the label is centered. - Note that a subgraph inherits attributes from its parent. Thus, if - the root graph sets labeljust to l, the subgraph inherits - this value. - - - - - - - - - - Top/bottom placement of graph and cluster labels. - If the attribute is t, place label at the top; - if the attribute is b, place label at the bottom. - By default, root - graph labels go on the bottom and cluster labels go on the top. - Note that a subgraph inherits attributes from its parent. Thus, if - the root graph sets labelloc to b, the subgraph inherits - this value. - - - - - - - - - - If the edge has a URL or labelURL - attribute, this attribute determines which window of the - browser is used - for the URL attached to the label. - Setting it to "_graphviz" will open a new window if it - doesn't already exist, or reuse it if it does. - If undefined, the value of the target is used. - - - - - - - - - - Tooltip annotation attached to label of an edge. - This is used only if the edge has a URL - or labelURL attribute. - - - - - - - - - - If true, the graph is rendered in landscape mode. Synonymous with - rotate=90 or - orientation=landscape. - - - - - - - - - - Specifies layers in which the node or edge is present. - - - - - - - - - - Specifies a linearly ordered list of layer names attached to the graph - The graph is then output in separate layers. Only those components - belonging to the current output layer appear. For more information, - see the page How to use drawing layers (overlays). - - - - - - - - - - Specifies the separator characters used to split the - layers attribute into a list of layer names. - - - - - - - - - - Specifies the name of the layout algorithm to use, such as dot - or neato. Normally, graphs should be kept independent of a type of - layout. In some cases, however, it can be convenient to embed the type - of layout desired within the graph. For example, a graph containing - position information from a layout might want to record what the - associated layout algorithm was. - - - This attribute takes precedence over - the -K flag - or the actual command name used. - - - - - - - - - - Preferred edge length, in inches. - - - - - - - - - - Specifies strictness of level constraints in neato - when mode="ipsep" or "hier". - Larger positive values mean stricter constraints, which demand more - separation between levels. On the other hand, negative values will relax - the constraints by allowing some overlap between the levels. - - - - - - - - - - Logical head of an edge. When compound is true, - if lhead is defined and is the name of a cluster containing - the real head, - the edge is clipped to the boundary of the cluster. - See undirected. - - - - - - - - - - Label position, in points. - The position indicates the center of the label. - - - - - - - - - - Logical tail of an edge. When compound is true, - if ltail is defined and is the name of a cluster - containing the real tail, - the edge is clipped to the boundary of the cluster. - See undirected. - - - - - - - - - - For graphs, this sets x and y margins of canvas, in inches. If the margin - is a single double, both margins are set equal to the given value. - - - Note that the margin is not part of the drawing but just empty space - left around the drawing. It basically corresponds to a translation of - drawing, as would be necessary to center a drawing on a page. Nothing - is actually drawn in the margin. To actually extend the background of - a drawing, see the pad attribute. - - - For nodes, this attribute specifies space left around the node's label. - By default, the value is 0.11,0.055. - - - - - - - - - - Sets the number of iterations used. - - - - - - - - - - Multiplicative scale factor used to alter the MinQuit (default = 8) - and MaxIter (default = 24) parameters used during crossing - minimization. These correspond to the - number of tries without improvement before quitting and the - maximum number of iterations in each pass. - - - - - - - - - - Specifies the minimum separation between all nodes. - - - - - - - - - - Minimum edge length (rank difference between head and tail). - - - - - - - - - - Technique for optimizing the layout. If mode is major, - neato uses stress majorization. If mode is KK, - neato uses a version of the gradient descent method. The only advantage - to the latter technique is that it is sometimes appreciably faster for - small (number of nodes < 100) graphs. A significant disadvantage is that - it may cycle. - - - There are two new, experimental modes in neato, hier, which adds a top-down - directionality similar to the layout used in dot, and ipsep, which - allows the graph to specify minimum vertical and horizontal distances - between nodes. (See the sep attribute.) - - - - - - - - - - This value specifies how the distance matrix is computed for the input - graph. The distance matrix specifies the ideal distance between every - pair of nodes. neato attemps to find a layout which best achieves - these distances. By default, it uses the length of the shortest path, - where the length of each edge is given by its len - attribute. If model is circuit, neato uses the - circuit resistance - model to compute the distances. This tends to emphasize clusters. If - model is subset, neato uses the subset model. This sets the - edge length to be the number of nodes that are neighbors of exactly one - of the end points, and then calculates the shortest paths. This helps - to separate nodes with high degree. - - - - - - - - - - If Graphviz is built with MOSEK defined, mode=ipsep and mosek=true, - the Mosek software (www.mosek.com) is use to solve the ipsep constraints. - - - - - - - - - - Minimum space between two adjacent nodes in the same rank, in inches. - - - - - - - - - - By default, the justification of multi-line labels is done within the - largest context that makes sense. Thus, in the label of a polygonal - node, a left-justified line will align with the left side of the node - (shifted by the prescribed margin). - In record nodes, left-justified - line will line up with the left side of the enclosing column of fields. - If nojustify is true, multi-line labels will be justified - in the context of itself. For example, if the attribute is set, - the first label line is long, and the second is shorter and left-justified, - the second will align with the left-most character in the first line, - regardless of how large the node might be. - - - - - - - - - - If set, normalize coordinates of final - layout so that the first point is at the origin, and then rotate the - layout so that the first edge is horizontal. - - - - - - - - - - Used to set number of iterations in - network simplex applications, used in - computing node x coordinates. - If defined, # iterations = nslimit * # nodes; - otherwise, # iterations = MAXINT. - - - - - - - - - - Used to set number of iterations in - network simplex applications, used for ranking nodes. - If defined, # iterations = nslimit1 * # nodes; - otherwise, # iterations = MAXINT. - - - - - - - - - - If "out" for a graph G, and n is a node in G, then edges n->* appear - left-to-right in the same order in which they are defined. - If "in", the edges *->n appear - left-to-right in the same order in which they are defined for all - nodes n. - - - - - - - - - - - - Specify order in which nodes and edges are drawn. - - - - - - - - - - Determines if and how node overlaps should be removed. Nodes are first - enlarged using the sep attribute. - If true, overlaps are retained. - If the value is scale, overlaps are removed by uniformly scaling in x and y. - If the value converts to false, node overlaps are removed by a - Voronoi-based technique. - If the value is scalexy, x and y are separately - scaled to remove overlaps. - If the value is orthoxy or orthoyx, overlaps - are moved by optimizing two constraint problems, one for the x axis and - one for the y. The suffix indicates which axis is processed first. - If the value is ortho, the technique is similar to orthoxy except a - heuristic is used to reduce the bias between the two passes. - If the value is ortho_yx, the technique is the same as ortho, except - the roles of x and y are reversed. - The values portho, porthoxy, porthoxy, and portho_yx are similar - to the previous four, except only pseudo-orthogonal ordering is - enforced. - - - If the value is compress, the layout will be scaled down as much as - possible without introducing any overlaps, obviously assuming there are - none to begin with. - - - If the value is ipsep, and the layout is done by neato with - mode="ipsep", the overlap removal constraints are - incorporated into the layout algorithm itself. - N.B. At present, this only supports one level of clustering. - - - If the value is vpsc, overlap removal is similarly to ortho, except - quadratic optimization is used to minimize node displacement. - N.B. At present, this mode only works when mode="ipsep". - - - Except for fdp, the layouts assume overlap="true" as the default. - Fdp first uses a number of passes using built-in, force-directed technique - to remove overlaps. Thus, fdp accepts overlap with an integer - prefix followed by a colon, specifying the number of tries. If there is - no prefix, no initial tries will be performed. If there is nothing following - a colon, none of the above methods will be attempted. By default, fdp - uses overlap="9:portho". Note that overlap="true", - overlap="0:true" and overlap="0:" all turn off all overlap - removal. - - - Except for the Voronoi method, all of these transforms preserve the - orthogonal ordering of the original layout. That is, if the x coordinates - of two nodes are originally the same, they will remain the same, and if - the x coordinate of one node is originally less than the x coordinate of - another, this relation will still hold in the transformed layout. The - similar properties hold for the y coordinates. - This is not quite true for the "porth*" cases. For these, orthogonal - ordering is only preserved among nodes related by an edge. - - - NOTEThe methods orthoxy and orthoyx are still evolving. The semantics of these may change, or these methods may disappear altogether. - - - - - - - - - - This is true if the value of pack is true (case-insensitive) or a - non-negative integer. If true, each connected component of the graph is - laid out separately, and then the graphs are packed tightly. - If pack has an integral value, this is used as the size, - in points, of - a margin around each part; otherwise, a default margin of 8 is used. - If pack is interpreted as false, the entire graph is laid out together. - The granularity and method of packing is influenced by the - packmode attribute. - - - For layouts which always do packing, such a twopi, the pack - attribute is just used to set the margin. - - - - - - - - - - This indicates the granularity and method used for packing - (cf. packMode). Note that defining - packmode will automatically turn on packing as though one had - set pack=true. - - - - - - - - - - The pad attribute specifies how much, in inches, to extend the - drawing area around the minimal area needed to draw the graph. - If the pad is a single double, both the x and y pad values are set - equal to the given value. This area is part of the - drawing and will be filled with the background color, if appropriate. - - - Normally, a small pad is used for aesthetic reasons, especially when - a background color is used, to avoid having nodes and edges abutting - the boundary of the drawn region. - - - - - - - - - - Width and height of output pages, in inches. If this is set and is - smaller than the size of the layout, a rectangular array of pages of - the specified page size is overlaid on the layout, with origins - aligned in the lower-left corner, thereby partitioning the layout - into pages. The pages are then produced one at a time, in - pagedir order. - - - At present, this only works for PostScript output. For other types of - output, one should use another tool to split the output into multiple - output files. Or use the viewport to generate - multiple files. - - - - - - - - - - If the page attribute is set and applicable, - this attribute specifies the order in which the pages are emitted. - This is limited to one of the 8 row or column major orders. - - - - - - - - - - Color used to draw the bounding box around a cluster. - If pencolor is not defined, color is - used. If this is not defined, bgcolor is used. - If this is not defined, the default is used. - - - Note that a cluster inherits the root graph's attributes if defined. - Thus, if the root graph has defined a pencolor, this will override a - color or bgcolor attribute set for the cluster. - - - - - - - - - - Set number of peripheries used in polygonal shapes and cluster - boundaries. Note that - user-defined shapes are treated as a - form of box shape, so the default - peripheries value is 1 and the user-defined shape will be drawn in - a bounding rectangle. Setting peripheries=0 will turn this off. - Also, 1 is the maximum peripheries value for clusters. - - - - - - - - - - If true and the node has a pos attribute on input, neato prevents the - node from moving from the input position. This property can also be specified - in the pos attribute itself (cf. the point type). - - - - - - - - - - Position of node, or spline control points. - For nodes, the position indicates the center of the node. - On output, the coordinates are in points. - - - In neato and fdp, pos can be used to set the initial position of a node. - By default, the coordinates are assumed to be in inches. However, the - -s command line flag can be used to specify - different units. - - - When the -n command line flag is used with - neato, it is assumed the positions have been set by one of the layout - programs, and are therefore in points. Thus, neato -n can accept - input correctly without requiring a -s flag and, in fact, - ignores any such flag. - - - - - - - - - - If quantum > 0.0, node label dimensions - will be rounded to integral multiples of the quantum. - - - - - - - - - - Rank constraints on the nodes in a subgraph. - If same, all nodes are placed on the same rank. - If min, all nodes are placed on the minimum rank. - If source, all nodes are placed on the minimum rank, and - the only nodes on the minimum rank belong to some subgraph whose - rank attribute is "source" or "min". - Analogous criteria hold for rank=max and rank=sink. - (Note: the - minimum rank is topmost or leftmost, and the maximum rank is bottommost - or rightmost.) - - - - - - - - - - Sets direction of graph layout. For example, if rankdir="LR", - and barring cycles, an edge T -> H; will go - from left to right. By default, graphs are laid out from top to bottom. - - - - - - - - - - In dot, this gives the desired rank separation, in inches. This is - the minimum vertical distance between the bottom of the nodes in one - rank and the tops of nodes in the next. If the value - contains "equally", the centers of all ranks are spaced equally apart. - Note that both - settings are possible, e.g., ranksep = "1.2 equally". - In twopi, specifies radial separation of concentric circles. - - - - - - - - - - Sets the aspect ratio (drawing height/drawing width) for the drawing. - Note that this is adjusted before - the size attribute constraints are enforced. - - - If ratio is numeric, it is taken as the desired aspect ratio. - Then, if the actual aspect ratio is less than the desired ratio, - the drawing height is scaled up to achieve the - desired ratio; if the actual ratio is greater than that desired ratio, - the drawing width is scaled up. - - - If ratio = fill and the size - attribute is set, node positions are scaled, separately in both x - and y, so that the final drawing exactly fills the specified size. - - - If ratio = compress and the size - attribute is set, dot attempts to compress the initial layout to fit - in the given size. This achieves a tighter packing of nodes but - reduces the balance and symmetry. This feature only works in dot. - - - If ratio = expand, the size - attribute is set, and both the width and the height of the graph are - less than the value in size, node positions are scaled - uniformly until at least - one dimension fits size exactly. - Note that this is distinct from using size as the - desired size, as here the drawing is expanded before edges are generated and - all node and text sizes remain unchanged. - - - If ratio = auto, the page - attribute is set and the graph cannot be drawn on a single page, - then size is set to an ``ideal'' value. - In particular, the size in a given dimension will be the smallest integral - multiple of the page size in that dimension which is at least half the - current size. The two dimensions are then scaled independently to the - new size. This feature only works in dot. - - - - - - - - - - Rectangles for fields of records, in points. - - - - - - - - - - If true, force polygon to be regular. - - - - - - - - - - If true and there are multiple clusters, run cross - minimization a second time. - - - - - - - - - - This is a synonym for the dpi attribute. - - - - - - - - - - This specifies nodes to be used as the center of the - layout and the root of the generated spanning tree. As a graph attribute, - this gives the name of the node. As a node attribute (circo only), it - specifies that the node should be used as a central node. In twopi, - this will actually be the central node. In circo, the block containing - the node will be central in the drawing of its connected component. - If not defined, - twopi will pick a most central node, and circo will pick a random node. - - - - - - - - - - If 90, set drawing orientation to landscape. - - - - - - - - - - Edges with the same head and the same samehead value are aimed - at the same point on the head. - See undirected. - - - - - - - - - - Edges with the same tail and the same sametail value are aimed - at the same point on the tail. - See undirected. - - - - - - - - - - If the input graph defines the vertices - attribute, and output is dot or xdot, this gives - the number of points used for a node whose shape is a circle or ellipse. - It plays the same role in neato, when adjusting the layout to avoid - overlapping nodes, and in image maps. - - - - - - - - - - During network simplex, maximum number of edges with negative cut values - to search when looking for one with minimum cut value. - - - - - - - - - - Fraction to increase polygons (multiply - coordinates by 1 + sep) for purposes of determining overlap. Guarantees - a minimal non-zero distance between nodes. - If unset but esep is defined, sep will be - set to esep/0.8. If esep is unset, the default value - is used. - - - When overlap="ipsep" or "vpsc", - sep gives a minimum distance, in inches, to be left between nodes. - In this case, if sep is a pointf, the x and y separations can be - specified separately. - - - - - - - - - - Set the shape of a node. - - - - - - - - - - (Deprecated) If defined, shapefile specifies a file containing user-supplied node content. - The shape of the node is set to box. - The image in the shapefile must be - rectangular. The image formats supported as well as the precise semantics of - how the file is used depends on the - output format. For further details, see - External PostScript files. - - - There is one exception to this usage. - If shape is set to "epsf", shapefile gives - a filename containing a definition of the node in PostScript. - The graphics defined must be contain all of the - node content, including any desired boundaries. - For further details, see - - External PostScript files. - - - - - - - - - - Print guide boxes in PostScript at the beginning of - routesplines if 1, or at the end if 2. (Debugging) - - - - - - - - - - Number of sides if shape=polygon. - - - - - - - - - - Maximum width and height of drawing, in inches. - If defined and the drawing is too large, the drawing is uniformly - scaled down so that it fits within the given size. - - - If size ends in an exclamation point (!), - then it is taken to be - the desired size. In this case, if both dimensions of the drawing are - less than size, the drawing is scaled up uniformly until at - least one dimension equals its dimension in size. - - - Note that there is some interaction between the size and - ratio attributes. - - - - - - - - - - Skew factor for shape=polygon. Positive values - skew top of polygon to right; negative to left. - - - - - - - - - - Controls how, and if, edges are represented. If true, edges are drawn as - splines routed around nodes; if false, edges are drawn as line segments. - If set to "", no edges are drawn at all. - - - (1 March 2007) The values line and spline can be - used as synonyms for false and true, respectively. - In addition, the value polyline specifies that edges should be - drawn as polylines. - - - By default, the attribute is unset. How this is interpreted depends on - the layout. For dot, the default is to draw edges as splines. For all - other layouts, the default is to draw edges as line segments. Note that - for these latter layouts, if splines="true", this - requires non-overlapping nodes (cf. overlap). - If fdp is used for layout and splines="compound", then the edges are - drawn to avoid clusters as well as nodes. - - - - - - - - - - Parameter used to determine the initial layout of nodes. If unset, the - nodes are randomly placed in a unit square with - the same seed is always used for the random number generator, so the - initial placement is repeatable. - - - - - - - - - - Set style for node or edge. For cluster subgraph, if "filled", the - cluster box's background is filled. - - - - - - - - - - A URL or pathname specifying an XML style sheet, used in SVG output. - - - - - - - - - - If tailURL is defined, it is - output as part of the tail label of the edge. - Also, this value is used near the tail node, overriding any - URL value. - See undirected. - - - - - - - - - - If true, the tail of an edge is clipped to the boundary of the tail node; - otherwise, the end of the edge goes to the center of the node, or the - center of a port, if applicable. - - - - - - - - - - Synonym for tailURL. - - - - - - - - - - Text label to be placed near tail of edge. - See undirected. - - - - - - - - - - Indicates where on the tail node to attach the tail of the edge. - See undirected. - - - - - - - - - - If the edge has a tailURL, - this attribute determines which window of the - browser is used - for the URL. Setting it to "_graphviz" will open a new window if it - doesn't already exist, or reuse it if it does. - If undefined, the value of the target is used. - - - - - - - - - - Tooltip annotation attached to the tail of an edge. This is used only - if the edge has a tailURL attribute. - - - - - - - - - - If the object has a URL, this attribute determines which window - of the browser is used for the URL. - See W3C documentation. - - - - - - - - - - Tooltip annotation attached to the node or edge. If unset, Graphviz - will use the object's label if defined. - Note that if the label is a record specification or an HTML-like - label, the resulting tooltip may be unhelpful. In this case, if - tooltips will be generated, the user should set a tooltip - attribute explicitly. - - - - - - - - - - If set explicitly to true or false, the value determines whether or not - internal bitmap rendering relies on a truecolor color model or uses - a color palette. - If the attribute is unset, truecolor is not used - unless there is a shapefile property - for some node in the graph. - The output model will use the input model when possible. - - - Use of color palettes results in less memory usage during creation of the - bitmaps and smaller output files. - - - Usually, the only time it is necessary to specify the truetype model - is if the graph uses more than 256 colors. - However, if one uses bgcolor=transparent with - a color palette, font - antialiasing can show up as a fuzzy white area around characters. - Using truecolor=true avoids this problem. - - - - - - - - - - If the input graph defines this attribute, the node is polygonal, - and output is dot or xdot, this attribute provides the - coordinates of the vertices of the node's polygon, in inches. - If the node is an ellipse or circle, the - samplepoints attribute affects - the output. - - - - - - - - - - Clipping window on final drawing. - - - - - - - - - - Factor to scale up drawing to allow margin for expansion in - Voronoi technique. dim' = (1+2*margin)*dim. - - - - - - - - - - Weight of edge. In dot, the heavier the weight, the shorter, - straighter and more vertical the edge is. In neato, the heavier the - weight, the more neato will try to place the end points so that the - length of the edge is len. - - - - - - - - - - Width of node, in inches. This is taken as the initial, minimum width - of the node. If fixedsize is true, this - will be the final width of the node. Otherwise, if the node label - requires more width to fit, the node's width will be increased to - contain the label. Note also that, if the output format is dot, the - value given to width will be the final value. - - - - - - - - - - Provides z coordinate value for 3D layouts and displays. If the - graph has dim set to 3 (or more), - neato will use a node's z value - for the z coordinate of its initial position if - its pos attribute is also defined. - - - Even if no z values are specified in the input, it is necessary to - declare a z attribute for nodes, e.g, using node[z=""] - in order to get z values on output. - Thus, setting dim=3 but not declaring z will - cause neato -Tvrml to - layout the graph in 3D but project the layout onto the xy-plane - for the rendering. If the z attribute is declared, the final rendering - will be in 3D. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Resources/Dockerfile b/tests/Resources/Dockerfile new file mode 100644 index 0000000..cf9f4d6 --- /dev/null +++ b/tests/Resources/Dockerfile @@ -0,0 +1,7 @@ +FROM php:8.2 + +RUN apt-get update \ + && apt-get install -y graphviz \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/project diff --git a/tests/phpDocumentor/GraphViz/Test/AttributeTest.php b/tests/phpDocumentor/GraphViz/Test/AttributeTest.php index 3c1d5a4..de02e1f 100644 --- a/tests/phpDocumentor/GraphViz/Test/AttributeTest.php +++ b/tests/phpDocumentor/GraphViz/Test/AttributeTest.php @@ -21,8 +21,7 @@ */ class AttributeTest extends TestCase { - /** @var Attribute */ - protected $fixture = null; + protected Attribute $fixture; /** * Initializes the fixture for this test. diff --git a/tests/phpDocumentor/GraphViz/Test/AttributesTest.php b/tests/phpDocumentor/GraphViz/Test/AttributesTest.php new file mode 100644 index 0000000..464b461 --- /dev/null +++ b/tests/phpDocumentor/GraphViz/Test/AttributesTest.php @@ -0,0 +1,110 @@ +setAttribute('color', 'red'); + $attribute = $obj->getAttribute('color'); + + $this->assertSame('color', $attribute->getKey()); + $this->assertSame('red', $attribute->getValue()); + } + + public function testGetAttributeThrowsExceptionIfNotFound(): void + { + $obj = new class() { + use Attributes; + }; + + $this->expectException(AttributeNotFound::class); + $obj->getAttribute('missing'); + } + + public function testMagicSet(): void + { + $obj = new class() { + use Attributes; + }; + + $result = $obj->setColor('blue'); + + $this->assertSame($obj, $result); + } + + public function testMagicGet(): void + { + $obj = new class() { + use Attributes; + }; + $obj->setColor('blue'); + + $attribute = $obj->getColor(); + + $this->assertInstanceOf(Attribute::class, $attribute); + $this->assertSame('color', $attribute->getKey()); + $this->assertSame('blue', $attribute->getValue()); + } + + public function testMagicCallReturnsNullForUnknownMethod(): void + { + $obj = new class() { + use Attributes; + }; + + $this->assertNull($obj->fooBar('baz')); + } + + public function testSetAttributeThenMagicGet(): void + { + $obj = new class() { + use Attributes; + }; + + $obj->setAttribute('mainShape', 'box'); + $attribute = $obj->getMainShape(); + + $this->assertInstanceOf(Attribute::class, $attribute); + $this->assertSame('mainShape', $attribute->getKey()); + $this->assertSame('box', $attribute->getValue()); + } + + public function testMagicSetThenGetAttribute(): void + { + $obj = new class() { + use Attributes; + }; + + $obj->setMainShape('box'); + $attribute = $obj->getAttribute('mainShape'); + + $this->assertInstanceOf(Attribute::class, $attribute); + $this->assertSame('mainShape', $attribute->getKey()); + $this->assertSame('box', $attribute->getValue()); + } +} diff --git a/tests/phpDocumentor/GraphViz/Test/EdgeTest.php b/tests/phpDocumentor/GraphViz/Test/EdgeTest.php index 4145e0a..584bf00 100644 --- a/tests/phpDocumentor/GraphViz/Test/EdgeTest.php +++ b/tests/phpDocumentor/GraphViz/Test/EdgeTest.php @@ -110,8 +110,8 @@ public function testCall(): void { $label = 'my label'; $fixture = new Edge(new Node('from'), new Node('to')); - $this->assertInstanceOf(Edge::class, $fixture->setLabel($label)); - $this->assertSame($label, $fixture->getLabel()->getValue()); + $this->assertInstanceOf(Edge::class, $fixture->setAttribute('label', $label)); + $this->assertSame($label, $fixture->getAttribute('label')->getValue()); $this->assertNull($fixture->someNonExcistingMethod()); } @@ -126,7 +126,7 @@ public function testGetNonExistingAttributeThrowsAttributeNotFound(): void $this->expectException(AttributeNotFound::class); $this->expectExceptionMessage('Attribute with name "label" was not found'); - $fixture->getLabel(); + $fixture->getAttribute('label'); } /** @@ -138,8 +138,8 @@ public function testGetNonExistingAttributeThrowsAttributeNotFound(): void public function testToString(): void { $fixture = new Edge(new Node('from'), new Node('to')); - $fixture->setLabel('MyLabel'); - $fixture->setWeight(45); + $fixture->setAttribute('label', 'MyLabel'); + $fixture->setAttribute('weight', 45); $dot = << "to" [ diff --git a/tests/phpDocumentor/GraphViz/Test/GraphTest.php b/tests/phpDocumentor/GraphViz/Test/GraphTest.php index 242d01d..7e56151 100644 --- a/tests/phpDocumentor/GraphViz/Test/GraphTest.php +++ b/tests/phpDocumentor/GraphViz/Test/GraphTest.php @@ -31,12 +31,11 @@ use const PHP_EOL; /** - * Test for the the class representing a GraphViz graph. + * Test for the class representing a GraphViz graph. */ class GraphTest extends TestCase { - /** @var Graph */ - protected $fixture; + protected Graph $fixture; /** * Sets up the fixture, for example, opens a network connection. @@ -207,8 +206,8 @@ public function testSetPath(): void public function test__call(): void { $this->assertNull($this->fixture->MyMethod()); - $this->assertSame($this->fixture, $this->fixture->setBgColor('black')); - $this->assertSame('black', $this->fixture->getBgColor()->getValue()); + $this->assertSame($this->fixture, $this->fixture->setAttribute('bgColor', 'black')); + $this->assertSame('black', $this->fixture->getAttribute('bgColor')->getValue()); } /** @@ -218,7 +217,7 @@ public function test__call(): void public function testGetNonExistingAttributeThrowsAttributeNotFound(): void { $this->expectException(AttributeNotFound::class); - $this->expectExceptionMessage('Attribute with name "notexisting" was not found'); + $this->expectExceptionMessage('Attribute with name "notExisting" was not found'); $this->fixture->getNotExisting(); } @@ -312,29 +311,29 @@ public function testFindNode(): void } /** - * @covers \phpDocumentor\GraphViz\Graph::__set + * @covers \phpDocumentor\GraphViz\Graph::getNode */ - public function test__set(): void + public function testGetNode(): void { - $mock = m::mock(Node::class); - - $this->fixture->__set('myNode', $mock); + $node = new Node('My'); + $this->fixture->setNode($node); - self::assertSame($mock, $this->fixture->myNode); + $this->assertSame( + $node, + $this->fixture->getNode('My'), + ); } /** - * @covers \phpDocumentor\GraphViz\Graph::__get + * @covers \phpDocumentor\GraphViz\Graph::getNode */ - public function test__get(): void + public function testGetNodeNodeDoesNotExist(): void { - $mock = m::mock(Node::class); + $node = new Node('My'); + $this->fixture->setNode($node); - $this->fixture->myNode = $mock; - $this->assertSame( - $mock, - $this->fixture->myNode - ); + $this->expectException(\LogicException::class); + $this->fixture->getNode('Alien'); } /** @@ -400,7 +399,7 @@ public function test__toString(): void $this->normalizeLineEndings(('digraph "My First Graph" {' . PHP_EOL . PHP_EOL . '}')) ); - $graph->setLabel('PigeonPost'); + $graph->setAttribute('label', 'PigeonPost'); $this->assertSame( $this->normalizeLineEndings((string) $graph), $this->normalizeLineEndings(('digraph "My First Graph" {' . PHP_EOL . 'label="PigeonPost"' . PHP_EOL . '}')) diff --git a/tests/phpDocumentor/GraphViz/Test/NodeTest.php b/tests/phpDocumentor/GraphViz/Test/NodeTest.php index 5fe1316..1a7ba83 100644 --- a/tests/phpDocumentor/GraphViz/Test/NodeTest.php +++ b/tests/phpDocumentor/GraphViz/Test/NodeTest.php @@ -22,8 +22,7 @@ */ class NodeTest extends TestCase { - /** @var Node */ - protected $fixture = null; + protected Node $fixture; /** * Initializes the fixture for this test. @@ -47,7 +46,7 @@ public function testConstruct(): void $fixture ); $this->assertSame('MyName', $fixture->getName()); - $this->assertSame('MyLabel', $fixture->getLabel()->getValue()); + $this->assertSame('MyLabel', $fixture->getAttribute('label')->getValue()); } /** @@ -101,8 +100,8 @@ public function testName(): void public function testCall(): void { $fontname = 'Bitstream Vera Sans'; - $this->assertInstanceOf(Node::class, $this->fixture->setfontname($fontname)); - $this->assertSame($fontname, $this->fixture->getfontname()->getValue()); + $this->assertInstanceOf(Node::class, $this->fixture->setAttribute('fontname', $fontname)); + $this->assertSame($fontname, $this->fixture->getAttribute('fontname')->getValue()); $this->assertNull($this->fixture->someNonExistingMethod()); } @@ -115,7 +114,7 @@ public function testGetNonExistingAttributeThrowsAttributeNotFound(): void $this->expectException(AttributeNotFound::class); $this->expectExceptionMessage('Attribute with name "fontname" was not found'); - $this->fixture->getFontname(); + $this->fixture->getAttribute('fontname'); } /** @@ -126,8 +125,8 @@ public function testGetNonExistingAttributeThrowsAttributeNotFound(): void */ public function testToString(): void { - $this->fixture->setfontsize(12); - $this->fixture->setfontname('Bitstream Vera Sans'); + $this->fixture->setAttribute('fontsize', 12); + $this->fixture->setAttribute('fontname', 'Bitstream Vera Sans'); $dot = <<fixture->setfontsize(12); - $this->fixture->setfontname('Bitstream Vera Sans'); + $this->fixture->setAttribute('fontsize', 12); + $this->fixture->setAttribute('fontname', 'Bitstream Vera Sans'); $this->fixture->setLabel('\phpDocumentor\Descriptor\ProjectDescriptor'); $dot = <<fixture = new MethodReflectionExtension(); - } - - /** - * @dataProvider existingMethodProvider - */ - public function testNodeHasMethodReturnsTrue(string $className, string $methodName): void - { - $classReflection = m::mock(ClassReflection::class); - $classReflection->shouldReceive('getName')->andReturn($className); - - $this->assertTrue($this->fixture->hasMethod($classReflection, $methodName)); - } - - /** - * @return array> - */ - public function existingMethodProvider(): array - { - return [ - 'node::getLabel' => [ - 'className' => Node::class, - 'methodName' => 'getLabel', - ], - 'node::setLabel' => [ - 'className' => Node::class, - 'methodName' => 'setLabel', - ], - 'graph::setFontSize' => [ - 'className' => Graph::class, - 'methodName' => 'setFontSize', - ], - 'graph::getFontSize' => [ - 'className' => Graph::class, - 'methodName' => 'getFontSize', - ], - ]; - } - - public function testAttributeType(): void - { - $classReflection = m::mock(ClassReflection::class); - $classReflection->shouldReceive('getName')->andReturn(Node::class); - - $method = $this->fixture->getMethod($classReflection, 'setFontSize'); - - $this->assertInstanceOf(FloatType::class, $method->getVariants()[0]->getParameters()[0]->getType()); - } - - public function testAttributeTypeOfNoneExisting(): void - { - $classReflection = m::mock(ClassReflection::class); - $classReflection->shouldReceive('getName')->andReturn(Node::class); - - $method = $this->fixture->getMethod($classReflection, 'setColor'); - - $this->assertInstanceOf(StringType::class, $method->getVariants()[0]->getParameters()[0]->getType()); - } -}