pax_global_header00006660000000000000000000000064137657634040014531gustar00rootroot0000000000000052 comment=cc8b3f2e9b4b3c788470f1ded24f6e6163279151 phpunit-complexity-2.0.2/000077500000000000000000000000001376576340400154145ustar00rootroot00000000000000phpunit-complexity-2.0.2/.github/000077500000000000000000000000001376576340400167545ustar00rootroot00000000000000phpunit-complexity-2.0.2/.github/FUNDING.yml000066400000000000000000000000321376576340400205640ustar00rootroot00000000000000github: sebastianbergmann phpunit-complexity-2.0.2/.github/workflows/000077500000000000000000000000001376576340400210115ustar00rootroot00000000000000phpunit-complexity-2.0.2/.github/workflows/ci.yml000066400000000000000000000070311376576340400221300ustar00rootroot00000000000000# https://help.github.com/en/categories/automating-your-workflow-with-github-actions on: - "pull_request" - "push" name: "CI" jobs: coding-guidelines: name: "Coding Guidelines" runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Run friendsofphp/php-cs-fixer" run: "php7.4 ./tools/php-cs-fixer fix --diff-format=udiff --dry-run --show-progress=dots --using-cache=no --verbose" type-checker: name: "Type Checker" runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Update dependencies with composer" run: "php7.4 ./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run vimeo/psalm" run: "php7.4 ./tools/psalm --config=.psalm/config.xml --no-progress --shepherd --show-info=false --stats" backward-compatibility: name: Backward Compatibility runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Fetch tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: php-version: 7.4 coverage: none extensions: intl tests: name: "Tests" runs-on: "ubuntu-latest" strategy: fail-fast: false matrix: php-version: - "7.3" - "7.4" - "8.0" - "8.1" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Install PHP with extensions" uses: "shivammathur/setup-php@v2" with: php-version: "${{ matrix.php-version }}" coverage: "pcov" - name: "Cache dependencies installed with composer" uses: "actions/cache@v1" with: path: "~/.composer/cache" key: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}" restore-keys: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install dependencies with composer" run: "./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run tests with phpunit/phpunit" run: "vendor/bin/phpunit --coverage-clover=coverage.xml" - name: "Send code coverage report to Codecov.io" env: CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" run: "bash <(curl -s https://codecov.io/bash) || true" mutation-tests: name: "Mutation Tests" runs-on: "ubuntu-latest" strategy: matrix: php-version: - "7.4" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Install PHP with extensions" uses: "shivammathur/setup-php@v2" with: php-version: "${{ matrix.php-version }}" coverage: "pcov" - name: "Cache dependencies installed with composer" uses: "actions/cache@v1" with: path: "~/.composer/cache" key: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}" restore-keys: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install dependencies with composer" run: "./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run mutation tests with infection/infection" run: "./tools/infection --show-mutations --only-covered" phpunit-complexity-2.0.2/.gitignore000066400000000000000000000001151376576340400174010ustar00rootroot00000000000000/.idea /.php_cs /.php_cs.cache /.phpunit.result.cache /composer.lock /vendor phpunit-complexity-2.0.2/.phive/000077500000000000000000000000001376576340400166055ustar00rootroot00000000000000phpunit-complexity-2.0.2/.phive/phars.xml000066400000000000000000000010341376576340400204420ustar00rootroot00000000000000 phpunit-complexity-2.0.2/.php_cs.dist000066400000000000000000000207031376576340400176350ustar00rootroot00000000000000 For the full copyright and license information, please view the LICENSE file that was distributed with this source code. EOF; $finder = PhpCsFixer\Finder::create() ->files() ->in(__DIR__ . '/src') ->in(__DIR__ . '/tests/unit') ->in(__DIR__ . '/tests/integration'); return PhpCsFixer\Config::create() ->setFinder($finder) ->setRiskyAllowed(true) ->setRules([ 'align_multiline_comment' => true, 'array_indentation' => true, 'array_syntax' => ['syntax' => 'short'], 'binary_operator_spaces' => [ 'operators' => [ '=' => 'align_single_space_minimal', '=>' => 'align_single_space_minimal', ], ], 'blank_line_after_namespace' => true, 'blank_line_before_statement' => [ 'statements' => [ 'break', 'continue', 'declare', 'default', 'die', 'do', 'exit', 'for', 'foreach', 'goto', 'if', 'include', 'include_once', 'require', 'require_once', 'return', 'switch', 'throw', 'try', 'while', 'yield', ], ], 'braces' => true, 'cast_spaces' => true, 'class_attributes_separation' => ['elements' => ['const', 'method', 'property']], 'combine_consecutive_issets' => true, 'combine_consecutive_unsets' => true, 'compact_nullable_typehint' => true, 'concat_space' => ['spacing' => 'one'], 'constant_case' => true, 'declare_equal_normalize' => ['space' => 'none'], 'declare_strict_types' => true, 'dir_constant' => true, 'elseif' => true, 'encoding' => true, 'explicit_indirect_variable' => true, 'explicit_string_variable' => true, 'full_opening_tag' => true, 'fully_qualified_strict_types' => true, 'function_declaration' => true, 'global_namespace_import' => [ 'import_classes' => true, 'import_constants' => true, 'import_functions' => true, ], 'header_comment' => ['header' => $header, 'separate' => 'none'], 'heredoc_to_nowdoc' => true, 'increment_style' => [ 'style' => PhpCsFixer\Fixer\Operator\IncrementStyleFixer::STYLE_POST, ], 'indentation_type' => true, 'is_null' => true, 'line_ending' => true, 'list_syntax' => ['syntax' => 'short'], 'logical_operators' => true, 'lowercase_keywords' => true, 'lowercase_static_reference' => true, 'magic_constant_casing' => true, 'magic_method_casing' => true, 'method_argument_space' => ['ensure_fully_multiline' => true], 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, 'multiline_whitespace_before_semicolons' => true, 'native_constant_invocation' => false, 'native_function_casing' => false, 'native_function_invocation' => false, 'native_function_type_declaration_casing' => true, 'new_with_braces' => false, 'no_alias_functions' => true, 'no_alternative_syntax' => true, 'no_blank_lines_after_class_opening' => true, 'no_blank_lines_after_phpdoc' => true, 'no_blank_lines_before_namespace' => true, 'no_closing_tag' => true, 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, 'no_extra_blank_lines' => true, 'no_homoglyph_names' => true, 'no_leading_import_slash' => true, 'no_leading_namespace_whitespace' => true, 'no_mixed_echo_print' => ['use' => 'print'], 'no_multiline_whitespace_around_double_arrow' => true, 'no_null_property_initialization' => true, 'no_php4_constructor' => true, 'no_short_bool_cast' => true, 'no_short_echo_tag' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_spaces_after_function_name' => true, 'no_spaces_around_offset' => true, 'no_spaces_inside_parenthesis' => true, 'no_superfluous_elseif' => true, 'no_superfluous_phpdoc_tags' => [ 'allow_mixed' => true, ], 'no_trailing_comma_in_list_call' => true, 'no_trailing_comma_in_singleline_array' => true, 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'no_unneeded_control_parentheses' => true, 'no_unneeded_curly_braces' => true, 'no_unneeded_final_method' => true, '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, 'no_whitespace_in_blank_line' => true, 'non_printable_character' => true, 'normalize_index_brace' => true, 'object_operator_without_whitespace' => true, 'ordered_class_elements' => [ 'order' => [ 'use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public_static', 'property_protected_static', 'property_private_static', 'property_public', 'property_protected', 'property_private', 'method_public_static', 'construct', 'destruct', 'magic', 'phpunit', 'method_public', 'method_protected', 'method_private', 'method_protected_static', 'method_private_static', ], ], 'ordered_imports' => [ 'imports_order' => [ PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CONST, PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_FUNCTION, PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CLASS, ] ], 'ordered_interfaces' => [ 'direction' => 'ascend', 'order' => 'alpha', ], 'phpdoc_add_missing_param_annotation' => false, 'phpdoc_align' => true, 'phpdoc_annotation_without_dot' => true, 'phpdoc_indent' => true, 'phpdoc_no_access' => true, 'phpdoc_no_empty_return' => true, 'phpdoc_no_package' => true, 'phpdoc_order' => true, 'phpdoc_return_self_reference' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, 'phpdoc_single_line_var_spacing' => true, 'phpdoc_summary' => true, 'phpdoc_to_comment' => true, 'phpdoc_trim' => true, 'phpdoc_trim_consecutive_blank_line_separation' => true, 'phpdoc_types' => ['groups' => ['simple', 'meta']], 'phpdoc_types_order' => true, 'phpdoc_var_without_name' => true, 'pow_to_exponentiation' => true, 'protected_to_private' => true, 'return_assignment' => true, 'return_type_declaration' => ['space_before' => 'none'], 'self_accessor' => true, 'self_static_accessor' => true, 'semicolon_after_instruction' => true, 'set_type_to_cast' => true, 'short_scalar_cast' => true, 'simple_to_complex_string_variable' => true, 'simplified_null_return' => false, 'single_blank_line_at_eof' => true, 'single_import_per_statement' => true, 'single_line_after_imports' => true, 'single_quote' => true, 'standardize_not_equals' => true, 'strict_param' => true, 'ternary_to_null_coalescing' => true, 'trailing_comma_in_multiline_array' => true, 'trim_array_spaces' => true, 'unary_operator_spaces' => true, 'visibility_required' => [ 'elements' => [ 'const', 'method', 'property', ], ], 'void_return' => true, 'whitespace_after_comma_in_array' => true, ]); phpunit-complexity-2.0.2/.psalm/000077500000000000000000000000001376576340400166065ustar00rootroot00000000000000phpunit-complexity-2.0.2/.psalm/baseline.xml000066400000000000000000000001571376576340400211150ustar00rootroot00000000000000 phpunit-complexity-2.0.2/.psalm/config.xml000066400000000000000000000007521376576340400206010ustar00rootroot00000000000000 phpunit-complexity-2.0.2/ChangeLog.md000066400000000000000000000016001376576340400175620ustar00rootroot00000000000000# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [2.0.2] - 2020-10-26 ### Fixed * `SebastianBergmann\Complexity\Exception` now correctly extends `\Throwable` ## [2.0.1] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [2.0.0] - 2020-07-25 ### Removed * The `ParentConnectingVisitor` has been removed (it should have been marked as `@internal`) ## [1.0.0] - 2020-07-22 * Initial release [2.0.2]: https://github.com/sebastianbergmann/complexity/compare/2.0.1...2.0.2 [2.0.1]: https://github.com/sebastianbergmann/complexity/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/complexity/compare/1.0.0...2.0.0 [1.0.0]: https://github.com/sebastianbergmann/complexity/compare/70ee0ad32d9e2be3f85beffa3e2eb474193f2487...1.0.0 phpunit-complexity-2.0.2/LICENSE000066400000000000000000000030161376576340400164210ustar00rootroot00000000000000sebastian/complexity Copyright (c) 2020, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. phpunit-complexity-2.0.2/README.md000066400000000000000000000020341376576340400166720ustar00rootroot00000000000000# sebastian/complexity Library for calculating the complexity of PHP code units. [![Latest Stable Version](https://img.shields.io/packagist/v/sebastian/complexity.svg?style=flat-square)](https://packagist.org/packages/sebastian/complexity) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.3-8892BF.svg?style=flat-square)](https://php.net/) [![CI Status](https://github.com/sebastianbergmann/complexity/workflows/CI/badge.svg?branch=master&event=push)](https://phpunit.de/build-status.html) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/complexity/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/complexity) ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/complexity ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/complexity ``` phpunit-complexity-2.0.2/build.xml000066400000000000000000000020241376576340400172330ustar00rootroot00000000000000 phpunit-complexity-2.0.2/composer.json000066400000000000000000000017011376576340400201350ustar00rootroot00000000000000{ "name": "sebastian/complexity", "description": "Library for calculating the complexity of PHP code units", "type": "library", "homepage": "https://github.com/sebastianbergmann/complexity", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues" }, "require": { "php": ">=7.3", "nikic/php-parser": "^4.7" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "config": { "platform": { "php": "7.3.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-master": "2.0-dev" } } } phpunit-complexity-2.0.2/infection.json000066400000000000000000000011301376576340400202600ustar00rootroot00000000000000{ "source": { "directories": [ "src" ] }, "mutators": { "@default": true, "TrueValue": { "ignore": [ "SebastianBergmann\\Complexity\\Calculator::calculateForAbstractSyntaxTree" ] }, "InstanceOf_": { "ignore": [ "SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor::classMethodName", "SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor::functionName" ] } }, "minMsi": 100, "minCoveredMsi": 100 } phpunit-complexity-2.0.2/phpunit.xml000066400000000000000000000015121376576340400176240ustar00rootroot00000000000000 tests src phpunit-complexity-2.0.2/src/000077500000000000000000000000001376576340400162035ustar00rootroot00000000000000phpunit-complexity-2.0.2/src/Calculator.php000066400000000000000000000046431376576340400210140ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use PhpParser\Error; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver; use PhpParser\NodeVisitor\ParentConnectingVisitor; use PhpParser\Parser; use PhpParser\ParserFactory; final class Calculator { /** * @throws RuntimeException */ public function calculateForSourceFile(string $sourceFile): ComplexityCollection { return $this->calculateForSourceString(file_get_contents($sourceFile)); } /** * @throws RuntimeException */ public function calculateForSourceString(string $source): ComplexityCollection { try { $nodes = $this->parser()->parse($source); assert($nodes !== null); return $this->calculateForAbstractSyntaxTree($nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } // @codeCoverageIgnoreEnd } /** * @param Node[] $nodes * * @throws RuntimeException */ public function calculateForAbstractSyntaxTree(array $nodes): ComplexityCollection { $traverser = new NodeTraverser; $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(true); $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ParentConnectingVisitor); $traverser->addVisitor($complexityCalculatingVisitor); try { /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } // @codeCoverageIgnoreEnd return $complexityCalculatingVisitor->result(); } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } } phpunit-complexity-2.0.2/src/Complexity/000077500000000000000000000000001376576340400203405ustar00rootroot00000000000000phpunit-complexity-2.0.2/src/Complexity/Complexity.php000066400000000000000000000015311376576340400232060ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; /** * @psalm-immutable */ final class Complexity { /** * @var string */ private $name; /** * @var int */ private $cyclomaticComplexity; public function __construct(string $name, int $cyclomaticComplexity) { $this->name = $name; $this->cyclomaticComplexity = $cyclomaticComplexity; } public function name(): string { return $this->name; } public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } } phpunit-complexity-2.0.2/src/Complexity/ComplexityCollection.php000066400000000000000000000027221376576340400252250ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function count; use Countable; use IteratorAggregate; /** * @psalm-immutable */ final class ComplexityCollection implements Countable, IteratorAggregate { /** * @psalm-var list */ private $items = []; public static function fromList(Complexity ...$items): self { return new self($items); } /** * @psalm-param list $items */ private function __construct(array $items) { $this->items = $items; } /** * @psalm-return list */ public function asArray(): array { return $this->items; } public function getIterator(): ComplexityCollectionIterator { return new ComplexityCollectionIterator($this); } public function count(): int { return count($this->items); } public function isEmpty(): bool { return empty($this->items); } public function cyclomaticComplexity(): int { $cyclomaticComplexity = 0; foreach ($this as $item) { $cyclomaticComplexity += $item->cyclomaticComplexity(); } return $cyclomaticComplexity; } } phpunit-complexity-2.0.2/src/Complexity/ComplexityCollectionIterator.php000066400000000000000000000020451376576340400267350ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use Iterator; final class ComplexityCollectionIterator implements Iterator { /** * @psalm-var list */ private $items; /** * @var int */ private $position = 0; public function __construct(ComplexityCollection $items) { $this->items = $items->asArray(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return isset($this->items[$this->position]); } public function key(): int { return $this->position; } public function current(): Complexity { return $this->items[$this->position]; } public function next(): void { $this->position++; } } phpunit-complexity-2.0.2/src/Exception/000077500000000000000000000000001376576340400201415ustar00rootroot00000000000000phpunit-complexity-2.0.2/src/Exception/Exception.php000066400000000000000000000005551376576340400226150ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use Throwable; interface Exception extends Throwable { } phpunit-complexity-2.0.2/src/Exception/RuntimeException.php000066400000000000000000000006031376576340400241530ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; final class RuntimeException extends \RuntimeException implements Exception { } phpunit-complexity-2.0.2/src/Visitor/000077500000000000000000000000001376576340400176425ustar00rootroot00000000000000phpunit-complexity-2.0.2/src/Visitor/ComplexityCalculatingVisitor.php000066400000000000000000000055611376576340400262460ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function assert; use function is_array; use PhpParser\Node; use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Trait_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; final class ComplexityCalculatingVisitor extends NodeVisitorAbstract { /** * @psalm-var list */ private $result = []; /** * @var bool */ private $shortCircuitTraversal; public function __construct(bool $shortCircuitTraversal) { $this->shortCircuitTraversal = $shortCircuitTraversal; } public function enterNode(Node $node): ?int { if (!$node instanceof ClassMethod && !$node instanceof Function_) { return null; } if ($node instanceof ClassMethod) { $name = $this->classMethodName($node); } else { $name = $this->functionName($node); } $statements = $node->getStmts(); assert(is_array($statements)); $this->result[] = new Complexity( $name, $this->cyclomaticComplexity($statements) ); if ($this->shortCircuitTraversal) { return NodeTraverser::DONT_TRAVERSE_CHILDREN; } return null; } public function result(): ComplexityCollection { return ComplexityCollection::fromList(...$this->result); } /** * @param Stmt[] $statements */ private function cyclomaticComplexity(array $statements): int { $traverser = new NodeTraverser; $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor; $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($statements); return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } private function classMethodName(ClassMethod $node): string { $parent = $node->getAttribute('parent'); assert($parent instanceof Class_ || $parent instanceof Trait_); assert(isset($parent->namespacedName)); assert($parent->namespacedName instanceof Name); return $parent->namespacedName->toString() . '::' . $node->name->toString(); } private function functionName(Function_ $node): string { assert(isset($node->namespacedName)); assert($node->namespacedName instanceof Name); return $node->namespacedName->toString(); } } phpunit-complexity-2.0.2/src/Visitor/CyclomaticComplexityCalculatingVisitor.php000066400000000000000000000032221376576340400302460ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function get_class; use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\LogicalAnd; use PhpParser\Node\Expr\BinaryOp\LogicalOr; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Catch_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\While_; use PhpParser\NodeVisitorAbstract; final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract { /** * @var int */ private $cyclomaticComplexity = 1; public function enterNode(Node $node): void { /* @noinspection GetClassMissUseInspection */ switch (get_class($node)) { case BooleanAnd::class: case BooleanOr::class: case Case_::class: case Catch_::class: case ElseIf_::class: case For_::class: case Foreach_::class: case If_::class: case LogicalAnd::class: case LogicalOr::class: case Ternary::class: case While_::class: $this->cyclomaticComplexity++; } } public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } } phpunit-complexity-2.0.2/tests/000077500000000000000000000000001376576340400165565ustar00rootroot00000000000000phpunit-complexity-2.0.2/tests/_fixture/000077500000000000000000000000001376576340400204035ustar00rootroot00000000000000phpunit-complexity-2.0.2/tests/_fixture/ExampleClass.php000066400000000000000000000020321376576340400234720ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity\TestFixture; final class ExampleClass { public function method(): void { if (true || false) { if (true && false) { for ($i = 0; $i <= 1; $i++) { $a = true ? 'foo' : 'bar'; } foreach (range(0, 1) as $i) { switch ($i) { case 0: break; case 1: break; default: } } } } elseif (null) { try { } catch (Throwable $t) { } } while (true) { } do { } while (false); } } phpunit-complexity-2.0.2/tests/_fixture/ExampleTrait.php000066400000000000000000000020241376576340400235110ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity\TestFixture; trait ExampleTrait { public function method(): void { if (true || false) { if (true && false) { for ($i = 0; $i <= 1; $i++) { $a = true ? 'foo' : 'bar'; } foreach (range(0, 1) as $i) { switch ($i) { case 0: break; case 1: break; default: } } } } elseif (null) { try { } catch (Throwable $t) { } } while (true) { } do { } while (false); } } phpunit-complexity-2.0.2/tests/_fixture/example_function.php000066400000000000000000000016251376576340400244600ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity\TestFixture; function example_function(): void { if (true or false) { if (true and false) { for ($i = 0; $i <= 1; $i++) { $a = true ? 'foo' : 'bar'; } foreach (range(0, 1) as $i) { switch ($i) { case 0: break; case 1: break; default: } } } } elseif (null) { try { } catch (Throwable $t) { } } while (true) { } do { } while (false); } phpunit-complexity-2.0.2/tests/integration/000077500000000000000000000000001376576340400211015ustar00rootroot00000000000000phpunit-complexity-2.0.2/tests/integration/CalculatorTest.php000066400000000000000000000060561376576340400245520ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function file_get_contents; use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Complexity\Calculator * @covers \SebastianBergmann\Complexity\ComplexityCalculatingVisitor * @covers \SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor * * @uses \SebastianBergmann\Complexity\Complexity * @uses \SebastianBergmann\Complexity\ComplexityCollection * @uses \SebastianBergmann\Complexity\ComplexityCollectionIterator * * @medium */ final class CalculatorTest extends TestCase { public function testCalculatesCyclomaticComplexityOfClassMethodInSourceFile(): void { $result = (new Calculator)->calculateForSourceFile(__DIR__ . '/../_fixture/ExampleClass.php')->asArray(); $this->assertSame('SebastianBergmann\Complexity\TestFixture\ExampleClass::method', $result[0]->name()); $this->assertSame(14, $result[0]->cyclomaticComplexity()); } public function testCalculatesCyclomaticComplexityOfTraitMethodInSourceFile(): void { $result = (new Calculator)->calculateForSourceFile(__DIR__ . '/../_fixture/ExampleTrait.php')->asArray(); $this->assertSame('SebastianBergmann\Complexity\TestFixture\ExampleTrait::method', $result[0]->name()); $this->assertSame(14, $result[0]->cyclomaticComplexity()); } public function testCalculatesCyclomaticComplexityOfFunctionInSourceFile(): void { $result = (new Calculator)->calculateForSourceFile(__DIR__ . '/../_fixture/example_function.php')->asArray(); $this->assertSame('SebastianBergmann\Complexity\TestFixture\example_function', $result[0]->name()); $this->assertSame(14, $result[0]->cyclomaticComplexity()); } public function testCalculatesCyclomaticComplexityInSourceString(): void { $result = (new Calculator)->calculateForSourceString(file_get_contents(__DIR__ . '/../_fixture/ExampleClass.php'))->asArray(); $this->assertSame('SebastianBergmann\Complexity\TestFixture\ExampleClass::method', $result[0]->name()); $this->assertSame(14, $result[0]->cyclomaticComplexity()); } public function testCalculatesCyclomaticComplexityInAbstractSyntaxTree(): void { $nodes = $this->parser()->parse(file_get_contents(__DIR__ . '/../_fixture/ExampleClass.php')); assert($nodes !== null); $result = (new Calculator)->calculateForAbstractSyntaxTree($nodes)->asArray(); $this->assertSame('SebastianBergmann\Complexity\TestFixture\ExampleClass::method', $result[0]->name()); $this->assertSame(14, $result[0]->cyclomaticComplexity()); } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } } phpunit-complexity-2.0.2/tests/unit/000077500000000000000000000000001376576340400175355ustar00rootroot00000000000000phpunit-complexity-2.0.2/tests/unit/ComplexityCalculatingVisitorTest.php000066400000000000000000000055021376576340400267740ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function file_get_contents; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver; use PhpParser\NodeVisitor\ParentConnectingVisitor; use PhpParser\NodeVisitorAbstract; use PhpParser\Parser; use PhpParser\ParserFactory; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Complexity\ComplexityCalculatingVisitor * * @uses \SebastianBergmann\Complexity\Complexity * @uses \SebastianBergmann\Complexity\ComplexityCollection * @uses \SebastianBergmann\Complexity\ComplexityCollectionIterator * @uses \SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor * * @small */ final class ComplexityCalculatingVisitorTest extends TestCase { /** * @dataProvider shortCircuitTraversalProvider */ public function testCalculatesComplexityForAbstractSyntaxTree(bool $shortCircuitTraversal): void { $nodes = $this->parser()->parse( file_get_contents(__DIR__ . '/../_fixture/ExampleClass.php') ); $traverser = new NodeTraverser; $complexityCalculatingVisitor = new ComplexityCalculatingVisitor($shortCircuitTraversal); $shortCircuitVisitor = new class extends NodeVisitorAbstract { private $numberOfNodesVisited = 0; public function enterNode(Node $node): void { $this->numberOfNodesVisited++; } public function numberOfNodesVisited(): int { return $this->numberOfNodesVisited; } }; $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ParentConnectingVisitor); $traverser->addVisitor($complexityCalculatingVisitor); $traverser->addVisitor($shortCircuitVisitor); /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($nodes); $this->assertSame(14, $complexityCalculatingVisitor->result()->cyclomaticComplexity()); if ($shortCircuitTraversal) { $this->assertSame(9, $shortCircuitVisitor->numberOfNodesVisited()); } else { $this->assertSame(70, $shortCircuitVisitor->numberOfNodesVisited()); } } public function shortCircuitTraversalProvider(): array { return [ 'short-circuit traversal' => [true], 'no short-circuit traversal' => [false], ]; } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } } phpunit-complexity-2.0.2/tests/unit/ComplexityCollectionTest.php000066400000000000000000000040631376576340400252620ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Complexity\ComplexityCollection * @covers \SebastianBergmann\Complexity\ComplexityCollectionIterator * * @uses \SebastianBergmann\Complexity\Complexity * * @small */ final class ComplexityCollectionTest extends TestCase { /** * @psalm-var list */ private $array; protected function setUp(): void { $this->array = [ new Complexity('Class::method', 1), new Complexity('function', 2), ]; } /** * @testdox Can be created from list of Complexity objects */ public function testCanBeCreatedFromListOfObjects(): void { $collection = ComplexityCollection::fromList($this->array[0], $this->array[1]); $this->assertSame($this->array, $collection->asArray()); } public function testCanBeCounted(): void { $collection = ComplexityCollection::fromList($this->array[0], $this->array[1]); $this->assertCount(2, $collection); $this->assertFalse($collection->isEmpty()); } public function testCanBeIterated(): void { $array = []; foreach (ComplexityCollection::fromList($this->array[0], $this->array[1]) as $key => $value) { $array[$key] = $value; } $this->assertCount(2, $array); $this->assertArrayHasKey(0, $array); $this->assertSame($this->array[0], $array[0]); $this->assertArrayHasKey(1, $array); $this->assertSame($this->array[1], $array[1]); } public function testHasCyclomaticComplexity(): void { $collection = ComplexityCollection::fromList($this->array[0], $this->array[1]); $this->assertSame(3, $collection->cyclomaticComplexity()); } } phpunit-complexity-2.0.2/tests/unit/ComplexityTest.php000066400000000000000000000014761376576340400232530ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Complexity\Complexity * * @small */ final class ComplexityTest extends TestCase { public function testHasName(): void { $this->assertSame('Foo::bar', $this->complexity()->name()); } public function testHasCyclomaticComplexity(): void { $this->assertSame(1, $this->complexity()->cyclomaticComplexity()); } private function complexity(): Complexity { return new Complexity('Foo::bar', 1); } } phpunit-complexity-2.0.2/tests/unit/CyclomaticComplexityCalculatingVisitorTest.php000066400000000000000000000024511376576340400310040ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function file_get_contents; use PhpParser\Lexer; use PhpParser\NodeTraverser; use PhpParser\Parser; use PhpParser\ParserFactory; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor * * @small */ final class CyclomaticComplexityCalculatingVisitorTest extends TestCase { public function testCalculatesCyclomaticComplexityForAbstractSyntaxTree(): void { $nodes = $this->parser()->parse( file_get_contents(__DIR__ . '/../_fixture/example_function.php') ); $traverser = new NodeTraverser; $visitor = new CyclomaticComplexityCalculatingVisitor; $traverser->addVisitor($visitor); /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($nodes); $this->assertSame(14, $visitor->cyclomaticComplexity()); } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } }