diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml new file mode 100644 index 0000000..52f86df --- /dev/null +++ b/.github/workflows/coding-standards.yml @@ -0,0 +1,12 @@ +name: Coding Standards + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + cs: + uses: ray-di/.github/.github/workflows/coding-standards.yml@v1 + with: + php_version: 8.3 \ No newline at end of file diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..9eb813a --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,13 @@ +name: Continuous Integration + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + uses: ray-di/.github/.github/workflows/continuous-integration.yml@v1 + with: + old_stable: '["8.2", "8.3", "8.4"]' + current_stable: 8.5 \ No newline at end of file diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..79ea4f6 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,43 @@ +name: Static Analysis + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-interaction --no-progress --prefer-dist + + - name: Run PHPStan + run: ./vendor/bin/phpstan analyse -c phpstan.neon --no-progress --no-interaction --error-format=checkstyle | cs2pr \ No newline at end of file diff --git a/.gitignore b/.gitignore index 88e2d70..ea59c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ /composer.lock /phpunit.xml /.php_cs.cache +/.phpcs-cache .phpunit.result.cache /.phpunit.cache/ diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index e7a5cac..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,185 +0,0 @@ -exclude(['tests/Fake', 'tests/tmp', 'template']) - ->in(__DIR__); - -return \PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules([ - '@PSR2' => true, - '@PHP70Migration:risky' => true, - '@PHP71Migration:risky' => true, - 'align_multiline_comment' => true, - 'array_indentation' => true, - 'array_syntax' => ['syntax' => 'short'], - 'backtick_to_shell_exec' => true, - 'binary_operator_spaces' => true, // @Symfony - 'blank_line_after_opening_tag' => true, // @Symfony - 'blank_line_before_return' => true, - 'blank_line_before_statement' => ['statements' => ['break', 'continue', 'declare', 'return', 'throw']], // @Symfony - 'cast_spaces' => true, // @Symfony - 'class_attributes_separation' => ['elements' => ['const', 'method']], // @Symfony -// 'class_keyword_remove' => true, - 'combine_consecutive_issets' => true, - 'combine_consecutive_unsets' => true, -// 'comment_to_phpdoc' => true, - 'compact_nullable_typehint' => true, - 'concat_space' => ['spacing' => 'one'], // @Symfony -// 'date_time_immutable' => true, - 'declare_equal_normalize' => true, // @Symfony - 'dir_constant' => true, // @Symfony:risky - 'ereg_to_preg' => true, // @Symfony:risky - 'error_suppression' => true, // @Symfony:risky - 'escape_implicit_backslashes' => true, - 'explicit_indirect_variable' => true, - 'explicit_string_variable' => true, -// 'final_internal_class' => true, - 'fully_qualified_strict_types' => true, - 'function_to_constant' => true, // @Symfony:risky - 'function_typehint_space' => true, // @Symfony - 'general_phpdoc_annotation_remove' => ['author', 'category', 'package', 'copyright', 'version'], - 'header_comment' => ['header' => $header, 'comment_type' => 'comment'], - 'heredoc_to_nowdoc' => true, - 'include' => true, // @Symfony -// 'increment_style' => 'pre', // @Symfony - 'indentation_type' => true, - 'is_null' => ['use_yoda_style' => false], // @Symfony:risky - 'linebreak_after_opening_tag' => true, -// 'list_syntax' => true, -// 'logical_operators' => true, - 'lowercase_cast' => true, // @Symfony - 'lowercase_static_reference' => true, // @Symfony - 'magic_constant_casing' => true, -// 'mb_str_functions' => true, - 'method_chaining_indentation' => true, - 'method_separation' => true, - 'modernize_types_casting' => true, // @Symfony:risky - 'multiline_comment_opening_closing' => true, - 'multiline_whitespace_before_semicolons' => true, -// 'native_constant_invocation' => true, - 'native_function_casing' => true, // @Symfony -// 'native_function_invocation' => true, -// 'new_with_braces' => true, // @Symfony - 'no_alias_functions' => true, // @Symfony:risky - 'no_alternative_syntax' => true, - 'no_binary_string' => true, - 'no_blank_lines_after_class_opening' => true, // @Symfony - 'no_blank_lines_after_phpdoc' => true, // @Symfony -// 'no_blank_lines_before_namespace' => true, - 'no_empty_comment' => true, // @Symfony - 'no_empty_phpdoc' => true, // @Symfony - 'no_empty_statement' => true, // @Symfony - 'no_extra_blank_lines' => true, - 'no_homoglyph_names' => true, // @Symfony:risky - 'no_leading_import_slash' => true, // @Symfony - 'no_leading_namespace_whitespace' => true, // @Symfony - 'no_mixed_echo_print' => true, // @Symfony - 'no_multiline_whitespace_around_double_arrow' => true, // @Symfony - 'no_null_property_initialization' => true, - 'no_php4_constructor' => true, - 'no_short_bool_cast' => true, // @Symfony - 'no_short_echo_tag' => false, - 'no_singleline_whitespace_before_semicolons' => true, // @Symfony - 'no_spaces_around_offset' => true, // @Symfony - 'no_superfluous_elseif' => true, - 'no_superfluous_phpdoc_tags' => true, - 'no_trailing_comma_in_list_call' => true, // @Symfony - 'no_trailing_comma_in_singleline_array' => true, // @Symfony - 'no_trailing_whitespace' => true, - 'no_trailing_whitespace_in_comment' => true, - 'no_unneeded_control_parentheses' => true, // @Symfony - 'no_unneeded_curly_braces' => true, // @Symfony - 'no_unneeded_final_method' => true, // @Symfony - 'no_unreachable_default_argument_value' => true, - 'no_unset_on_property' => true, - 'no_unused_imports' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'no_whitespace_before_comma_in_array' => true, // @Symfony - 'no_whitespace_in_blank_line' => true, // @Symfony - 'non_printable_character' => true, // @Symfony - 'normalize_index_brace' => true, // @Symfony - 'not_operator_with_space' => false, - 'not_operator_with_successor_space' => true, - 'object_operator_without_whitespace' => true, // @Symfony - 'ordered_class_elements' => true, - 'ordered_imports' => true, - 'php_unit_construct' => true, // @Symfony:risky - 'php_unit_dedicate_assert' => true, - 'php_unit_expectation' => true, - 'php_unit_fqcn_annotation' => true, // @Symfony -// 'php_unit_internal_class' => true, // @Symfony] -// 'php_unit_mock' => true, - 'php_unit_namespaced' => true, - 'php_unit_no_expectation_annotation' => true, - 'php_unit_ordered_covers' => true, - 'php_unit_set_up_tear_down_visibility' => true, - 'php_unit_strict' => true, -// 'php_unit_test_annotation' => true, -// 'php_unit_test_case_static_method_calls' => true, -// 'php_unit_test_class_requires_covers' => true, -// 'phpdoc_add_missing_param_annotation' => true, - 'phpdoc_align' => true, // @Symfony] - 'phpdoc_annotation_without_dot' => true, // @Symfony] - 'phpdoc_indent' => true, // @Symfony] - 'phpdoc_inline_tag' => true, // @Symfony] - 'phpdoc_no_access' => true, // @Symfony] - 'phpdoc_no_alias_tag' => true, // @Symfony - 'phpdoc_no_empty_return' => true, // @Symfony - 'phpdoc_no_package' => true, // @Symfony -// 'phpdoc_no_useless_inheritdoc' => true, // @Symfony - 'phpdoc_order' => true, - 'phpdoc_return_self_reference' => true, // @Symfony - 'phpdoc_scalar' => true, // @Symfony - 'phpdoc_separation' => true, // @Symfony - 'phpdoc_single_line_var_spacing' => true, // @Symfony -// 'phpdoc_summary' => true, // @Symfony - 'phpdoc_to_comment' => true, // @Symfony - 'phpdoc_trim' => true, // @Symfony - 'phpdoc_trim_consecutive_blank_line_separation' => true, - 'phpdoc_types' => true, // @Symfony - 'phpdoc_types_order' => true, // @Symfony - 'phpdoc_var_without_name' => true, // @Symfony - 'protected_to_private' => true, - 'psr0' => true, - 'psr4' => true, // @Symfony:risky - 'return_assignment' => true, - 'return_type_declaration' => ['space_before' => 'one'], - 'self_accessor' => true, // @Symfony:risky - 'semicolon_after_instruction' => true, // @Symfony - 'set_type_to_cast' => true, // @Symfony:risky - 'short_scalar_cast' => true, // @Symfony:risky - 'simplified_null_return' => true, - 'single_blank_line_before_namespace' => true, // @Symfony -// 'single_line_comment_style' => true, // @Symfony - 'single_line_after_imports' => true, - 'single_quote' => true, // @Symfony - 'space_after_semicolon' => true, // @Symfony - 'standardize_increment' => true, // @Symfony - 'standardize_not_equals' => true, // @Symfony -// 'static_lambda' => true, -// 'strict_comparison' => true, - 'strict_param' => true, -// 'string_line_ending' => true, - 'switch_case_semicolon_to_colon' => true, - 'switch_case_space' => true, - 'ternary_operator_spaces' => true, -// 'trailing_comma_in_multiline_array' => true, // @Symfony - 'trim_array_spaces' => true, // @Symfony - 'unary_operator_spaces' => true, // @Symfony - 'visibility_required' => true, -// 'void_return' => true, // @PHP71Migration:risky - 'whitespace_after_comma_in_array' => true, // @Symfony -// 'yoda_style' => true // @Symfony - ]) - ->setFinder($finder); diff --git a/composer.json b/composer.json index 22394c0..e253c7d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "ray/object-visual-grapher", - "description": "", + "description": "A visual graph generator for Ray.Di object dependency graphs", "license": "MIT", "authors": [ { @@ -12,14 +12,21 @@ "php": "^8.2" }, "require-dev": { - "phpunit/phpunit": "^10.5", - "squizlabs/php_codesniffer": "^3.2", - "friendsofphp/php-cs-fixer": "^2.11", + "bear/package": "^1.9.6", + "bear/query-repository": "^1.13", + "doctrine/coding-standard": "^14.0", "phpmd/phpmd": "^2.6", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5", "ray/di": "^2.10|2.x-dev", - "bear/package": "^1.9.6", - "doctrine/annotations": "^1.8", - "phpstan/phpstan": "^0.12.5" + "slevomat/coding-standard": "^8.15", + "squizlabs/php_codesniffer": "^4.0" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": false + } }, "autoload": { "psr-4": { @@ -39,23 +46,28 @@ "php ./tests/bin/package.php && dot -T png ./tests/bin/package.dot -o ./tests/bin/package.png && open ./tests/bin/package.png" ], "test": [ - "vendor/bin/phpunit" + "phpunit" ], "tests": [ "@cs", "@test", - "vendor/bin/phpstan analyse -l max src -c phpstan.neon --no-progress" + "phpstan analyse -c phpstan.neon --no-progress" ], "coverage": [ - "php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage" + "php -dzend_extension=xdebug.so -dxdebug.mode=coverage ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage" ], "cs": [ - "vendor/bin/php-cs-fixer fix -v --dry-run", - "vendor/bin/phpcs --standard=phpcs.xml src;" + "phpcs --standard=./phpcs.xml src tests" ], "cs-fix": [ - "vendor/bin/php-cs-fixer fix -v", - "vendor/bin/phpcbf src tests" + "phpcbf src tests" ] + }, + "scripts-descriptions": { + "test": "Run unit tests", + "tests": "Run coding standards, tests, and static analysis", + "coverage": "Generate test coverage report with Xdebug", + "cs": "Check coding standards", + "cs-fix": "Fix coding standards violations" } } diff --git a/phpcs.xml b/phpcs.xml index 460d322..19042e4 100755 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,18 +1,73 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + src + tests + */tests/Fake/* + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + - + + + + + + + + \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon index e69de29..2de7ef5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 8 + phpVersion: 80200 + paths: + - src \ No newline at end of file diff --git a/src/Arrow.php b/src/Arrow.php index 19042ca..353f745 100644 --- a/src/Arrow.php +++ b/src/Arrow.php @@ -7,31 +7,23 @@ use Ray\Aop\ReflectionClass; use Ray\Di\ProviderInterface; +use function class_exists; + final class Arrow implements ArrowInterface { - /** - * @var array - */ + /** @var array */ public static $history; - /** - * @var string - */ + /** @var string */ public $class; - /** - * @var string - */ + /** @var string */ private $interface; - /** - * @var string - */ + /** @var string */ private $arrowHead; - /** - * @var bool - */ + /** @var bool */ private $isInvalid; public function __construct(string $fromId, string $toId, string $toClass) @@ -41,15 +33,14 @@ public function __construct(string $fromId, string $toId, string $toClass) $isProvider = class_exists($toClass) && (new ReflectionClass($toClass))->implementsInterface(ProviderInterface::class); $this->arrowHead = $isProvider ? 'arrowhead=onormalonormal' : 'arrowhead=onormal'; $index = $fromId . $toId . $toClass; - if (isset(self::$history[$index])) { - // log duplicated (mostly @depracated) bindings ( - // error_log(sprintf('from:%s to:%s class:%s index:%s ', $fromId, $toClass, $toClass, $index)); - } + // log duplicated (mostly @depracated) bindings ( + // error_log(sprintf('from:%s to:%s class:%s index:%s ', $fromId, $toClass, $toClass, $index)); + $this->isInvalid = isset(self::$history[$index]); self::$history[$index] = true; } - public function __toString() + public function __toString(): string { return $this->isInvalid ? '' : <<interface} -> {$this->class} [style=solid, arrowtail=none, {$this->arrowHead}] diff --git a/src/ArrowInterface.php b/src/ArrowInterface.php index beb93ab..cdcc9b4 100644 --- a/src/ArrowInterface.php +++ b/src/ArrowInterface.php @@ -6,5 +6,5 @@ interface ArrowInterface { - public function __toString(); + public function __toString(): string; } diff --git a/src/ClassId.php b/src/ClassId.php index 72fed29..292be16 100644 --- a/src/ClassId.php +++ b/src/ClassId.php @@ -6,8 +6,8 @@ final class ClassId { - public function __invoke(string $class) : string + public function __invoke(string $class): string { - return 'class_' . (new SnakeName)($class); + return 'class_' . (new SnakeName())($class); } } diff --git a/src/ClassNode.php b/src/ClassNode.php index 261089a..9db8cb9 100644 --- a/src/ClassNode.php +++ b/src/ClassNode.php @@ -4,36 +4,34 @@ namespace Ray\ObjectGrapher; +use ReflectionClass; +use ReflectionException; + +use function assert; +use function class_exists; +use function in_array; +use function str_replace; + +use const PHP_EOL; + final class ClassNode implements NodeInterface { - /** - * @var array - */ + /** @var array */ public static $ids = []; - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $namespace; - /** - * @var string - */ + /** @var string */ private $class; - /** - * @var string - */ + /** @var string */ private $settersTable = ''; - /** - * @var bool - */ + /** @var bool */ private $invalid = false; /** @@ -41,7 +39,7 @@ final class ClassNode implements NodeInterface * @param string $class class * @param array $setters * - * @throws \ReflectionException + * @throws ReflectionException */ public function __construct(string $id, string $class, array $setters) { @@ -50,10 +48,11 @@ public function __construct(string $id, string $class, array $setters) return; } + self::$ids[] = $id; $this->id = $id; assert(class_exists($class)); - $ref = new \ReflectionClass($class); + $ref = new ReflectionClass($class); $this->namespace = str_replace('\\', '\\\\', $ref->getNamespaceName()); $this->class = $ref->getShortName(); foreach ($setters as $setterMethod => $port) { @@ -61,7 +60,7 @@ public function __construct(string $id, string $class, array $setters) } } - public function __toString() + public function __toString(): string { $html = $this->invalid ? '' : /* @lang html */ <<< EOT {$this->id} [style=solid, margin=0.02, label= diff --git a/src/DependencyId.php b/src/DependencyId.php index 5877054..e1fb5a1 100644 --- a/src/DependencyId.php +++ b/src/DependencyId.php @@ -4,14 +4,18 @@ namespace Ray\ObjectGrapher; +use function class_exists; +use function sprintf; + final class DependencyId { - public function __invoke(string $interace, string $name) : string + public function __invoke(string $interace, string $name): string { if (class_exists($interace)) { - return (new ClassId)($interace); + return (new ClassId())($interace); } - $snakeName = new SnakeName; + + $snakeName = new SnakeName(); return sprintf('dependency_%s_%s', ($snakeName)($interace), ($snakeName)($name)); } diff --git a/src/Graph.php b/src/Graph.php index 3de5dc4..d30579c 100644 --- a/src/Graph.php +++ b/src/Graph.php @@ -4,6 +4,8 @@ namespace Ray\ObjectGrapher; +use const PHP_EOL; + final class Graph { /** @@ -20,12 +22,13 @@ final class Graph */ private $arrows; - public function __toString() + public function __toString(): string { $string = ''; foreach ($this->nodes as $node) { $string .= (string) $node . PHP_EOL; } + $string .= PHP_EOL; foreach ($this->arrows as $arrow) { $string .= (string) $arrow . PHP_EOL; @@ -34,12 +37,12 @@ public function __toString() return $string; } - public function addNode(NodeInterface $node) : void + public function addNode(NodeInterface $node): void { $this->nodes[] = $node; } - public function addArrow(ArrowInterface $arrow) : void + public function addArrow(ArrowInterface $arrow): void { $this->arrows[] = $arrow; } diff --git a/src/InstanceNode.php b/src/InstanceNode.php index f50202e..00baf98 100644 --- a/src/InstanceNode.php +++ b/src/InstanceNode.php @@ -6,26 +6,24 @@ use Ray\Di\Instance; +use function addslashes; +use function gettype; +use function str_replace; + +use const PHP_EOL; + final class InstanceNode implements NodeInterface { - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $interface; - /** - * @var string - */ + /** @var string */ private $type; - /** - * @var string - */ + /** @var string */ private $named; public function __construct(string $id, string $interface, string $named, Instance $instance) @@ -36,7 +34,7 @@ public function __construct(string $id, string $interface, string $named, Instan $this->type = gettype($instance->value); } - public function __toString() + public function __toString(): string { $html = /* @lang html */ <<< EOT {$this->id} [style=dashed, margin=0.02, label= diff --git a/src/InterfaceNode.php b/src/InterfaceNode.php index c9747c9..f35a52b 100644 --- a/src/InterfaceNode.php +++ b/src/InterfaceNode.php @@ -4,31 +4,30 @@ namespace Ray\ObjectGrapher; +use ReflectionClass; + +use function assert; +use function class_exists; +use function interface_exists; +use function str_replace; + +use const PHP_EOL; + final class InterfaceNode implements NodeInterface { - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $namespace; - /** - * @var string - */ + /** @var string */ private $interface; - /** - * @var string - */ + /** @var string */ private $named; - /** - * @var string - */ + /** @var string */ private $bgColor; public function __construct(string $id, string $interface, string $named) @@ -39,7 +38,7 @@ public function __construct(string $id, string $interface, string $named) $this->bgColor = $interface ? 'ffffff' : 'aaaaaa'; } - public function __toString() + public function __toString(): string { $html = /* @lang html */ <<< EOT {$this->id} [style=dashed, margin=0.02, label= @@ -56,15 +55,15 @@ public function __toString() return str_replace(PHP_EOL, '', $html); } - public function setInstance() : void + public function setInstance(): void { $this->interface = 'instance'; } - private function setNamespace(string $interface) : void + private function setNamespace(string $interface): void { assert(interface_exists($interface) || class_exists($interface)); - $ref = new \ReflectionClass($interface); + $ref = new ReflectionClass($interface); $this->namespace = str_replace('\\', '\\\\', $ref->getNamespaceName()); $this->interface = $ref->getShortName(); } diff --git a/src/MyDependency.php b/src/MyDependency.php index 4a33430..18d9734 100644 --- a/src/MyDependency.php +++ b/src/MyDependency.php @@ -10,32 +10,24 @@ final class MyDependency { - /** - * @var string - */ + /** @var string */ public $class; - /** - * @var string - */ + /** @var string */ public $classId; - /** - * @var null|Arguments - */ + /** @var Arguments|null */ public $arguments; - /** - * @var SetterMethod - */ + /** @var SetterMethod */ public $setterMethods; public function __construct(Dependency $dependency) { - $prop = new Prop; + $prop = new Prop(); $newInstance = $prop($dependency, 'newInstance'); $this->class = $prop($newInstance, 'class'); - $this->classId = (new ClassId)($this->class); + $this->classId = (new ClassId())($this->class); $this->arguments = $prop($newInstance, 'arguments'); $this->setterMethods = $prop($newInstance, 'setterMethods'); } diff --git a/src/NodeInterface.php b/src/NodeInterface.php index d4272ab..7c4e0ea 100644 --- a/src/NodeInterface.php +++ b/src/NodeInterface.php @@ -6,5 +6,5 @@ interface NodeInterface { - public function __toString(); + public function __toString(): string; } diff --git a/src/ObjectGrapher.php b/src/ObjectGrapher.php index 64c90e3..de66b76 100644 --- a/src/ObjectGrapher.php +++ b/src/ObjectGrapher.php @@ -12,58 +12,51 @@ use Ray\Di\DependencyInterface; use Ray\Di\DependencyProvider; use Ray\Di\Instance; +use ReflectionClass; +use ReflectionException; + +use function assert; +use function class_exists; +use function explode; +use function interface_exists; +use function sprintf; final class ObjectGrapher { - /** - * @var Prop - */ + /** @var Prop */ private $prop; - /** - * @var Container - */ + /** @var Container */ private $container; - /** - * @var Container - */ + /** @var Container */ private $analysisContainer; - /** - * @var array - */ + /** @var array */ private $dependencies = []; - /** - * @var Graph - */ + + /** @var Graph */ private $graph; - /** - * @var DependencyId - */ + /** @var DependencyId */ private $dependencyId; - /** - * @var ClassId - */ + /** @var ClassId */ private $classId; - /** - * @var SnakeName - */ + /** @var SnakeName */ private $snakeName; public function __construct() { - $this->prop = new Prop; - $this->graph = new Graph; - $this->dependencyId = new DependencyId; - $this->classId = new ClassId; - $this->snakeName = new SnakeName; + $this->prop = new Prop(); + $this->graph = new Graph(); + $this->dependencyId = new DependencyId(); + $this->classId = new ClassId(); + $this->snakeName = new SnakeName(); } - public function __invoke(AbstractModule $module) : string + public function __invoke(AbstractModule $module): string { $this->init(); $this->container = $module->getContainer(); @@ -75,42 +68,48 @@ public function __invoke(AbstractModule $module) : string return $this->toString(); } - private function init() : void + private function init(): void { Arrow::$history = ToClass::$index = ClassNode::$ids = []; - $this->graph = new Graph; - $this->analysisContainer = new Container; + $this->graph = new Graph(); + $this->analysisContainer = new Container(); $this->dependencies = []; } - private function setGraph(string $type, string $name, DependencyInterface $dependency) : void + private function setGraph(string $type, string $name, DependencyInterface $dependency): void { $isTargetBinding = ! interface_exists($type); $dependencyId = ($this->dependencyId)($type, $name); if (! $isTargetBinding) { $this->graph->addNode(new InterfaceNode($dependencyId, $type, $name)); } + if ($dependency instanceof Instance) { $this->graph->addNode(new InstanceNode($dependencyId, $type, $name, $dependency)); } + if ($dependency instanceof Dependency) { $this->dependencyNode($dependencyId, new MyDependency($dependency), $isTargetBinding); } - if ($dependency instanceof DependencyProvider) { - $this->providerNode($dependencyId, $dependency); + + if (! ($dependency instanceof DependencyProvider)) { + return; } + + $this->providerNode($dependencyId, $dependency); } - private function dependencyNode(string $interfaceId, MyDependency $dependency, bool $isTargetBinding) : void + private function dependencyNode(string $interfaceId, MyDependency $dependency, bool $isTargetBinding): void { if (! $isTargetBinding) { $this->graph->addArrow(new ToClass($interfaceId, $dependency->classId)); } + $setters = $this->lineDependency($dependency); $this->graph->addNode(new ClassNode($dependency->classId, $dependency->class, $setters)); } - private function providerNode(string $interfaceId, DependencyProvider $dependency) : void + private function providerNode(string $interfaceId, DependencyProvider $dependency): void { $dependency = new MyDependency(($this->prop)($dependency, 'dependency')); $setters = $this->lineDependency($dependency); @@ -118,17 +117,17 @@ private function providerNode(string $interfaceId, DependencyProvider $dependenc if ($interfaceId) { $this->graph->addArrow(new ToProvider($interfaceId, $dependency->classId)); } + $this->graph->addNode(new ClassNode($dependency->classId, $dependency->class, $setters)); } - /** - * @return array setter symbol - */ - private function lineDependency(MyDependency $dependency) : array + /** @return array setter symbol */ + private function lineDependency(MyDependency $dependency): array { if (! $dependency->arguments) { return []; } + // constructor injection $arguments = ($this->prop)($dependency->arguments, 'arguments'); $port = sprintf('p_%s_construct', ($this->snakeName)($dependency->class)); @@ -148,9 +147,9 @@ private function lineDependency(MyDependency $dependency) : array return $setters; } - private function setterArrow(string $classPort, string $dependencyIndex) : void + private function setterArrow(string $classPort, string $dependencyIndex): void { - [$type, $name] = \explode('-', $dependencyIndex); + [$type, $name] = explode('-', $dependencyIndex); $dependencyId = ($this->dependencyId)($type, $name); $this->graph->addArrow(new Arrow($classPort, $dependencyId, $type)); if (class_exists($type)) { @@ -158,19 +157,21 @@ private function setterArrow(string $classPort, string $dependencyIndex) : void return; } + $this->graph->addNode(new InterfaceNode($dependencyId, $type, $name)); } - private function addClassNode(string $dependencyIndex) : void + private function addClassNode(string $dependencyIndex): void { - [$type, $name] = \explode('-', $dependencyIndex); + [$type, $name] = explode('-', $dependencyIndex); assert(class_exists($type)); - $isAbstract = (new \ReflectionClass($type))->isAbstract(); + $isAbstract = (new ReflectionClass($type))->isAbstract(); if ($isAbstract) { $this->graph->addNode(new InterfaceNode(($this->classId)($type), $type, $name)); return; } + $container = $this->container->getContainer(); if (isset($container[$dependencyIndex]) || isset($this->dependencies[$dependencyIndex])) { return; @@ -184,26 +185,24 @@ private function addClassNode(string $dependencyIndex) : void * @param string $classId class ID * @param string $port port ID */ - private function drawInjectionGraph(array $arguments, string $classId, string $port) : void + private function drawInjectionGraph(array $arguments, string $classId, string $port): void { foreach ($arguments as $argument) { - assert($argument instanceof Argument); $dependencyIndex = ($this->prop)($argument, 'index'); $classPort = sprintf('%s:%s:e', $classId, $port); $this->setterArrow($classPort, $dependencyIndex); } } - /** - * @throws \ReflectionException - */ - private function bindOnTheFly(string $dependencyIndex, string $type, string $name) : void + /** @throws ReflectionException */ + private function bindOnTheFly(string $dependencyIndex, string $type, string $name): void { /** @var class-string $type */ $bind = new Bind($this->analysisContainer, $type); if ($name !== '') { $bind->annotatedWith($name); } + $bind->to($type); $dependency = $this->analysisContainer->getContainer()[$dependencyIndex]; assert($dependency instanceof Dependency); @@ -212,7 +211,7 @@ private function bindOnTheFly(string $dependencyIndex, string $type, string $nam $this->graph->addNode(new ClassNode(($this->classId)($type), $type, $setters)); } - private function toString() : string + private function toString(): string { return <<getProperty($prop); - } catch (\ReflectionException $e) { + $property = (new ReflectionClass($object::class))->getProperty($prop); + } catch (ReflectionException) { return ''; } diff --git a/src/ProviderNode.php b/src/ProviderNode.php index 863f18a..d021ef6 100644 --- a/src/ProviderNode.php +++ b/src/ProviderNode.php @@ -4,21 +4,19 @@ namespace Ray\ObjectGrapher; +use function str_replace; + +use const PHP_EOL; + final class ProviderNode implements NodeInterface { - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $provider; - /** - * @var string - */ + /** @var string */ private $settersTable = ''; /** @@ -35,7 +33,7 @@ public function __construct(string $id, string $provider, array $setters) } } - public function __toString() + public function __toString(): string { $html = /* @lang html */ <<< EOT {$this->id} [style=solid, margin=0.02, label=< diff --git a/src/SnakeName.php b/src/SnakeName.php index 2d7aaa2..0900f62 100644 --- a/src/SnakeName.php +++ b/src/SnakeName.php @@ -4,9 +4,11 @@ namespace Ray\ObjectGrapher; +use function str_replace; + final class SnakeName { - public function __invoke(string $class) : string + public function __invoke(string $class): string { return str_replace('\\', '_', $class); } diff --git a/src/ToClass.php b/src/ToClass.php index d20b680..1f0b316 100644 --- a/src/ToClass.php +++ b/src/ToClass.php @@ -6,24 +6,16 @@ final class ToClass implements ArrowInterface { - /** - * @var array - */ + /** @var array */ public static $index; - /** - * @var string - */ + /** @var string */ public $class; - /** - * @var string - */ + /** @var string */ private $interface; - /** - * @var bool - */ + /** @var bool */ private $noArrow; public function __construct(string $interfaceId, string $classId) @@ -35,7 +27,7 @@ public function __construct(string $interfaceId, string $classId) self::$index[$indexId] = true; } - public function __toString() + public function __toString(): string { return $this->noArrow ? '' : <<interface} -> {$this->class} [style=dashed, arrowtail=none, arrowhead=onormal] diff --git a/src/ToProvider.php b/src/ToProvider.php index d1f21a3..ad5b1d9 100644 --- a/src/ToProvider.php +++ b/src/ToProvider.php @@ -6,14 +6,10 @@ final class ToProvider implements ArrowInterface { - /** - * @var string - */ + /** @var string */ public $class; - /** - * @var string - */ + /** @var string */ private $interface; public function __construct(string $interface, string $class) @@ -22,7 +18,7 @@ public function __construct(string $interface, string $class) $this->interface = $interface; } - public function __toString() + public function __toString(): string { return <<interface} -> {$this->class} [style=dashed, arrowtail=none, arrowhead=onormalonormal] diff --git a/tests/ObjectVisualGrapherTest.php b/tests/ObjectVisualGrapherTest.php index fb670a9..1ca1cb1 100644 --- a/tests/ObjectVisualGrapherTest.php +++ b/tests/ObjectVisualGrapherTest.php @@ -4,32 +4,31 @@ namespace Ray\ObjectGrapher; -use function array_keys; use BEAR\Package\PackageModule; use BEAR\Resource\Module\ResourceModule; use PHPUnit\Framework\TestCase; use Ray\Aop\Matcher; use Ray\Aop\Pointcut; +use function array_keys; + class ObjectVisualGrapherTest extends TestCase { - /** - * @var ObjectGrapher - */ + /** @var ObjectGrapher */ protected $objectGrapher; - protected function setUp() : void + protected function setUp(): void { - $this->objectGrapher = new ObjectGrapher; + $this->objectGrapher = new ObjectGrapher(); } - public function testIsInstanceOfObjectGrapher() : void + public function testIsInstanceOfObjectGrapher(): void { $actual = $this->objectGrapher; $this->assertInstanceOf(ObjectGrapher::class, $actual); } - public function test__invoke() : void + public function testInvoke(): void { $dot = ($this->objectGrapher)(new FakeModule()); $this->assertStringContainsString('dependency_Ray_ObjectGrapher_LoggerInterface_ -> class_Ray_ObjectGrapher_DatabaseLogger', $dot); @@ -38,7 +37,7 @@ public function test__invoke() : void $this->assertStringContainsString('dependency_Ray_ObjectGrapher_PdoInterface_ -> class_Ray_ObjectGrapher_PdoProvider [style=dashed, arrowtail=none, arrowhead=onormalonormal]', $dot); } - public function test__invokeBearResource() : void + public function testInvokeBearResource(): void { $dot = ($this->objectGrapher)(new ResourceModule('a')); $this->assertStringContainsString('dependency__BEAR_Resource_Annotation_AppName [style', $dot); @@ -46,17 +45,17 @@ public function test__invokeBearResource() : void $this->assertStringContainsString('dependency_BEAR_Resource_SchemeCollectionInterface_ -> class_BEAR_Resource_Module_SchemeCollectionProvider [style=dashed, arrowtail=none, arrowhead=onormalonormal]', $dot); } - public function test__invokeBearPackage() : void + public function testInvokeBearPackage(): void { - $dot = ($this->objectGrapher)(new PackageModule); + $dot = ($this->objectGrapher)(new PackageModule()); $this->assertStringContainsString('_BEAR_Sunday_Provide_Transfer_ConditionalResponseInterface_ -> class_BEAR_Sunday_Provide_Transfer_ConditionalResponse [style=dashed, arrowtail=none, arrowhead=onormal]', $dot); } - public function testConcreteDependencyAnalysisDoesNotMutateModule() : void + public function testConcreteDependencyAnalysisDoesNotMutateModule(): void { - $module = new ConcreteModule; + $module = new ConcreteModule(); $container = $module->getContainer(); - $container->addPointcut(new Pointcut((new Matcher)->any(), (new Matcher)->any(), [DatabaseLogger::class])); + $container->addPointcut(new Pointcut((new Matcher())->any(), (new Matcher())->any(), [DatabaseLogger::class])); $keys = array_keys($container->getContainer()); $pointcuts = $container->getPointcuts(); diff --git a/tests/bin/package.php b/tests/bin/package.php index da340ed..89c0ac6 100644 --- a/tests/bin/package.php +++ b/tests/bin/package.php @@ -9,6 +9,6 @@ use BEAR\Package\PackageModule; use Ray\ObjectGrapher\ObjectGrapher; -$dot = (new ObjectGrapher)(new AppMetaModule(new Meta('Ray\ObjectGrapher'), new PackageModule)); +$dot = (new ObjectGrapher())(new AppMetaModule(new Meta('Ray\ObjectGrapher'), new PackageModule())); $file = __DIR__ . '/package.dot'; file_put_contents($file, $dot);
{$this->provider}