diff --git a/.gitignore b/.gitignore index eb2fe83..cb38a10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /.idea/ /codecoverage/ /vendor/ +/var/ +php-analyzer.phar diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..32f058a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: php + +php: + - '7.1' + - '7.2' + - '7.3' + - '7.4snapshot' # beta1 + #- nightly #8 (symfony does not support php 8 yet) + +before_install: + - travis_retry composer install --no-interaction --prefer-source --dev + +script: + - vendor/bin/phpunit --testsuite=unit,integration --coverage-clover=coverage.xml + +after_success: + - bash <(curl -s https://codecov.io/bash) + +notifications: + email: + - chris@isfett.com diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1610ffa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +# How to contribute + +Thanks for considering to contribute to `php-analyzer`. While doing so please follow these guidelines: + + - You must follow the `isfett-coding-standard` ([link](https://www.github.com/isfett/coding-standard)). Add the phpcs and phpmd settings to your ide. If you use PhpStorm import the inspections.xml also. + - All non trivial features or bugfixes must have an associated issue for discussion. If you want to work on an issue that is already created, please leave a comment on it indicating that you are working on it. + - Add tests for features or bugfixes touching `src` code if you want to increase the chance of your contribution being merged. + - You must use [feature / topic branches](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows) to ease the merge of your contribution. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..87d2992 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Christopher Stenke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index fb3ce4e..343c03c 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ -php-analyzer +[![Build Status](https://img.shields.io/travis/isfett/php-analyzer/master?style=flat-square)](https://travis-ci.org/isfett/php-analyzer) +[![codecov](https://img.shields.io/codecov/c/github/isfett/php-analyzer?style=flat-square)](https://codecov.io/gh/isfett/php-analyzer) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg?style=flat-square)](https://php.net/) +[![Latest Stable Version](https://poser.pugx.org/isfett/php-analyzer/v/stable)](https://packagist.org/packages/isfett/php-analyzer) +[![Total Downloads](https://poser.pugx.org/isfett/php-analyzer/downloads)](https://packagist.org/packages/isfett/php-analyzer) +# PHP-Analyzer + +`php-analyzer` is a tool that aims to help you for different tasks. Mostly I found that I want to resolve them while doing my job. For details check the documented commands below. + +## Installation +Run +``` +$ composer global require isfett/php-analyzer +``` +or download the latest phar from [this repository](https://github.com/isfett/php-analyzer/releases). + +## Usage +For usages of the commands, check the documentation of each command. + +## Information +This tool uses [a php parser written in php](https://github.com/nikic/PHP-Parser), ignoring different code-style or whitespaces. + +## Commands +- [Most used conditions](docs/MostUsedConditions.md) Helps to check which conditions are used the most in your project. Just want to check if's? Or ternaries? No Problem! You can also split by logical operators, or split isset functions for each parameter. Including post-processing your conditions, flip-checking etc. You can find many examples within the link. + +## Planned +- Magic Number Detector +- Magic String Detector +- Find duplicate code (ignoring codestyle, just checking statements) + +## Contributing +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for more information. + +## License +The MIT License (MIT). Please see [LICENSE](LICENSE) for more information. \ No newline at end of file diff --git a/bin/phar.php b/bin/phar.php new file mode 100644 index 0000000..fda73e9 --- /dev/null +++ b/bin/phar.php @@ -0,0 +1,51 @@ +#!/usr/bin/env php +boot(); + +$finder = (new Finder()) + ->files() + ->in(dirname(__DIR__)) + ->notName('phar.php') + ->exclude(['codecoverage', 'docs', 'tests']); + +// create phar +$phar = new Phar($pharFilename); + +// creating our library using whole directory +$phar->buildFromIterator($finder->getIterator(), dirname(__DIR__)); + +// pointing main file which requires all classes +$phar->setStub( +"#!/usr/bin/env php +boot(); -$application = new Application(); -$application->run(); \ No newline at end of file +$container = $kernel->getContainer(); +$application = $container->get(Application::class); +$application->setDispatcher($dispatcher); +$application->run(); diff --git a/composer.json b/composer.json index d545689..92d4cb6 100644 --- a/composer.json +++ b/composer.json @@ -11,13 +11,23 @@ "support": { "issues": "https://github.com/isfett/php-analyzer/issues" }, + "bin": ["bin/php-analyzer", "bin/php-analyzer.bat", "bin/php-analyzer.php"], "require": { "php": "^7.1", - "symfony/console": "^4.0" + "symfony/console": "^4.0", + "symfony/finder": "^4.0", + "nikic/php-parser": "^4.2", + "symfony/dependency-injection": "^4.3", + "symfony/config": "^4.3", + "symfony/http-kernel": "^4.3", + "symfony/yaml": "^4.3", + "doctrine/collections": "^1.6", + "symfony/event-dispatcher": "^4.3", + "symfony/serializer": "4.4.x-dev" }, "require-dev": { "isfett/coding-standard": "^1.0", - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^7.0 | ^8.0" }, "autoload": { "psr-4": { @@ -28,5 +38,6 @@ "psr-4": { "Isfett\\PhpAnalyzer\\Tests\\": "tests/" } - } + }, + "minimum-stability": "dev" } diff --git a/composer.lock b/composer.lock index 987507e..864e911 100644 --- a/composer.lock +++ b/composer.lock @@ -4,35 +4,1026 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "245e559b036e603282b533b08663c353", + "content-hash": "acce545ddb012f623037b144bdc26a2f", "packages": [ + { + "name": "doctrine/collections", + "version": "1.6.x-dev", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "c5e0bc17b1620e97c968ac409acbff28b8b850be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/c5e0bc17b1620e97c968ac409acbff28b8b850be", + "reference": "c5e0bc17b1620e97c968ac409acbff28b8b850be", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan-shim": "^0.9.2", + "phpunit/phpunit": "^7.0", + "vimeo/psalm": "^3.2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", + "keywords": [ + "array", + "collections", + "iterators", + "php" + ], + "time": "2019-06-09T13:48:14+00:00" + }, + { + "name": "nikic/php-parser", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a1f72690ef18a21b168f19e45bd921aa7c394007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a1f72690ef18a21b168f19e45bd921aa7c394007", + "reference": "a1f72690ef18a21b168f19e45bd921aa7c394007", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2019-07-23T10:32:37+00:00" + }, { "name": "psr/container", - "version": "1.0.0", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "014d250daebff39eba15ba990eeb2a140798e77c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/014d250daebff39eba15ba990eeb2a140798e77c", + "reference": "014d250daebff39eba15ba990eeb2a140798e77c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2018-12-29T15:36:03+00:00" + }, + { + "name": "psr/log", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "c4421fcac1edd5a324fda73e589a5cf96e52ffd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/c4421fcac1edd5a324fda73e589a5cf96e52ffd0", + "reference": "c4421fcac1edd5a324fda73e589a5cf96e52ffd0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2018-11-21T13:42:00+00:00" + }, + { + "name": "symfony/config", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "5adc4a2ed1fca9f59aa609011d21fbeefbfb9c11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/5adc4a2ed1fca9f59aa609011d21fbeefbfb9c11", + "reference": "5adc4a2ed1fca9f59aa609011d21fbeefbfb9c11", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/filesystem": "^3.4|^4.0|^5.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<3.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/messenger": "^4.1|^5.0", + "symfony/service-contracts": "^1.1", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T07:11:23+00:00" + }, + { + "name": "symfony/console", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c6d17991875553ae4aa35cd34837e80c0dd55ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c6d17991875553ae4aa35cd34837e80c0dd55ec2", + "reference": "c6d17991875553ae4aa35cd34837e80c0dd55ec2", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/service-contracts": "^1.1" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T15:45:20+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "9dbb7bf728ba848ab5125d6d721d35bcf888960d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/9dbb7bf728ba848ab5125d6d721d35bcf888960d", + "reference": "9dbb7bf728ba848ab5125d6d721d35bcf888960d", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/container": "^1.0", + "symfony/service-contracts": "^1.1.2" + }, + "conflict": { + "symfony/config": "<4.3", + "symfony/finder": "<3.4", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "^4.3|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T07:11:23+00:00" + }, + { + "name": "symfony/error-handler", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "5e26808c4eecb418676881f8204b69b03122bd99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/5e26808c4eecb418676881f8204b69b03122bd99", + "reference": "5e26808c4eecb418676881f8204b69b03122bd99", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "^3.4|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "classmap": [ + "Resources/stubs/Debug.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ErrorHandler Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T06:19:29+00:00" + }, + { + "name": "symfony/error-renderer", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-renderer.git", + "reference": "1c9c90127e243762118bbdd6fe00ebba4a25ff39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-renderer/zipball/1c9c90127e243762118bbdd6fe00ebba4a25ff39", + "reference": "1c9c90127e243762118bbdd6fe00ebba4a25ff39", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<4.4" + }, + "require-dev": { + "symfony/console": "^4.4", + "symfony/dependency-injection": "^4.4", + "symfony/http-kernel": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorRenderer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Yonel Ceruto", + "email": "yonelceruto@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ErrorRenderer Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T12:17:02+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f77a8c216e6da734acb83e2fb5f3738e4020aae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f77a8c216e6da734acb83e2fb5f3738e4020aae3", + "reference": "f77a8c216e6da734acb83e2fb5f3738e4020aae3", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/event-dispatcher-contracts": "^1.1" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "1.1" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1", + "symfony/stopwatch": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2019-08-05T14:24:36+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "6dd7d743cb4141e634a5ec6082ef8f59ad90e4bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/6dd7d743cb4141e634a5ec6082ef8f59ad90e4bb", + "reference": "6dd7d743cb4141e634a5ec6082ef8f59ad90e4bb", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "suggest": { + "psr/event-dispatcher": "", + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-05-30T22:41:44+00:00" + }, + { + "name": "symfony/filesystem", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "bac0c4750953aaec251cb154d00fb1941d6e5d07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/bac0c4750953aaec251cb154d00fb1941d6e5d07", + "reference": "bac0c4750953aaec251cb154d00fb1941d6e5d07", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2019-08-05T14:07:36+00:00" + }, + { + "name": "symfony/finder", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "e535ef9c48e756c1bae9813a09016445bdd3bc6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/e535ef9c48e756c1bae9813a09016445bdd3bc6f", + "reference": "e535ef9c48e756c1bae9813a09016445bdd3bc6f", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2019-08-05T14:07:36+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "414f6b92f3608dd931974c18338cbc103cbf630f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/414f6b92f3608dd931974c18338cbc103cbf630f", + "reference": "414f6b92f3608dd931974c18338cbc103cbf630f", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mime": "^4.3|^5.0", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T07:13:58+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "45e89c11572952ca10d0fcc5caafe296dbdbdf07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/45e89c11572952ca10d0fcc5caafe296dbdbdf07", + "reference": "45e89c11572952ca10d0fcc5caafe296dbdbdf07", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/error-handler": "^4.4|^5.0", + "symfony/error-renderer": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-php73": "^1.9" + }, + "conflict": { + "symfony/browser-kit": "<4.3", + "symfony/config": "<3.4", + "symfony/console": ">=5", + "symfony/dependency-injection": "<4.3", + "symfony/translation": "<4.2", + "symfony/var-dumper": "<4.1.1", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "^4.3|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^4.3|^5.0", + "symfony/dom-crawler": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2|^5.0", + "symfony/translation-contracts": "^1.1", + "symfony/var-dumper": "^4.1.1|^5.0", + "twig/twig": "^1.34|^2.4" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T07:13:58+00:00" + }, + { + "name": "symfony/mime", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "1d47016bf0f2e4ee684c6f6434214bdab579b8d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/1d47016bf0f2e4ee684c6f6434214bdab579b8d7", + "reference": "1d47016bf0f2e4ee684c6f6434214bdab579b8d7", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "egulias/email-validator": "^2.0", + "symfony/dependency-injection": "^3.4|^4.1|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A library to manipulate MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "time": "2019-08-05T16:16:42+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" - } + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -40,76 +1031,58 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "compatibility", + "ctype", + "polyfill", + "portable" ], - "time": "2017-02-14T16:28:37+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { - "name": "symfony/console", - "version": "v4.3.2", + "name": "symfony/polyfill-intl-idn", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "b592b26a24265a35172d8a2094d8b10f22b7cc39" + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/b592b26a24265a35172d8a2094d8b10f22b7cc39", - "reference": "b592b26a24265a35172d8a2094d8b10f22b7cc39", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", + "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/var-dumper": "^4.3" + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.9" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" + "Symfony\\Polyfill\\Intl\\Idn\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -118,30 +1091,38 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Laurent Bassin", + "email": "laurent@bassin.info" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", "homepage": "https://symfony.com", - "time": "2019-06-13T11:03:18+00:00" + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -153,7 +1134,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -187,20 +1168,75 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "04ce3335667451138df4307d6a9b61565560199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", + "reference": "04ce3335667451138df4307d6a9b61565560199e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.11.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "d1fb4abcc0c47be136208ad9d68bf59f1ee17abd" + "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/d1fb4abcc0c47be136208ad9d68bf59f1ee17abd", - "reference": "d1fb4abcc0c47be136208ad9d68bf59f1ee17abd", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188", + "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188", "shasum": "" }, "require": { @@ -209,7 +1245,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -245,20 +1281,100 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/serializer", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "deb4c48eeb60b38b7d3499bbccbcccde3cc8778d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/deb4c48eeb60b38b7d3499bbccbcccde3cc8778d", + "reference": "deb4c48eeb60b38b7d3499bbccbcccde3cc8778d", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "phpdocumentor/type-resolver": "<0.2.1", + "symfony/dependency-injection": "<3.4", + "symfony/property-access": "<3.4", + "symfony/property-info": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/cache": "^3.4|^4.0|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/property-access": "^3.4|^4.0|^5.0", + "symfony/property-info": "^3.4.13|~4.0|^5.0", + "symfony/validator": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", + "doctrine/cache": "For using the default cached annotation reader and metadata cache.", + "psr/cache-implementation": "For using the metadata cache.", + "symfony/config": "For using the XML mapping loader.", + "symfony/http-foundation": "For using a MIME type guesser within the DataUriNormalizer.", + "symfony/property-access": "For using the ObjectNormalizer.", + "symfony/property-info": "To deserialize relations.", + "symfony/yaml": "For using the default YAML mapping loader." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Serializer Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T06:19:29+00:00" }, { "name": "symfony/service-contracts", - "version": "v1.1.5", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f391a00de78ec7ec8cf5cdcdae59ec7b883edb8d" + "reference": "3cd78bb952668d7bc1701f9599cf009353d50886" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f391a00de78ec7ec8cf5cdcdae59ec7b883edb8d", - "reference": "f391a00de78ec7ec8cf5cdcdae59ec7b883edb8d", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/3cd78bb952668d7bc1701f9599cf009353d50886", + "reference": "3cd78bb952668d7bc1701f9599cf009353d50886", "shasum": "" }, "require": { @@ -303,22 +1419,125 @@ "interoperability", "standards" ], - "time": "2019-06-13T11:15:36+00:00" - } - ], - "packages-dev": [ + "time": "2019-08-02T12:15:04+00:00" + }, + { + "name": "symfony/yaml", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "5743a65dad32ee7e2852dd2f8edeb39049ed4174" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/5743a65dad32ee7e2852dd2f8edeb39049ed4174", + "reference": "5743a65dad32ee7e2852dd2f8edeb39049ed4174", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-08-06T07:11:23+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/xdebug-handler", + "version": "1.3.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f", + "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "time": "2019-05-27T17:52:04+00:00" + }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "7c71fc2932158d00f24f10635bf3b3b8b6ee5b68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/7c71fc2932158d00f24f10635bf3b3b8b6ee5b68", + "reference": "7c71fc2932158d00f24f10635bf3b3b8b6ee5b68", "shasum": "" }, "require": { @@ -361,7 +1580,7 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2019-07-02T13:37:32+00:00" }, { "name": "isfett/coding-standard", @@ -369,16 +1588,16 @@ "source": { "type": "git", "url": "https://github.com/isfett/coding-standard.git", - "reference": "288f1169b54bc7ead72f9d0aea7f65e056ba8a16" + "reference": "c9d00340d41b0f358f5fdd5346d5596625320f61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/isfett/coding-standard/zipball/288f1169b54bc7ead72f9d0aea7f65e056ba8a16", - "reference": "288f1169b54bc7ead72f9d0aea7f65e056ba8a16", + "url": "https://api.github.com/repos/isfett/coding-standard/zipball/c9d00340d41b0f358f5fdd5346d5596625320f61", + "reference": "c9d00340d41b0f358f5fdd5346d5596625320f61", "shasum": "" }, "require": { - "php": "^7.1", + "php": "^7 | ^8", "phpmd/phpmd": "*", "slevomat/coding-standard": "~5", "squizlabs/php_codesniffer": "*" @@ -395,20 +1614,20 @@ } ], "description": "Coding-Standard for PHP", - "time": "2019-07-16T16:57:08+00:00" + "time": "2019-08-04T13:43:16+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.1", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + "reference": "6c3e9f1c8315a93bcebdd5f21630c945439968e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/6c3e9f1c8315a93bcebdd5f21630c945439968e9", + "reference": "6c3e9f1c8315a93bcebdd5f21630c945439968e9", "shasum": "" }, "require": { @@ -443,7 +1662,7 @@ "object", "object graph" ], - "time": "2019-04-07T13:18:21+00:00" + "time": "2019-08-05T15:28:34+00:00" }, { "name": "pdepend/pdepend", @@ -570,18 +1789,18 @@ "authors": [ { "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "role": "Developer", + "email": "arne@blankerts.de" }, { "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpeople.de" }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpunit.de" } ], "description": "Library for handling version information and constraints", @@ -741,31 +1960,34 @@ }, { "name": "phpmd/phpmd", - "version": "2.6.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "7425e155cf22cdd2b4dd3458a7da4cf6c0201562" + "reference": "b487b11f75025fb1a91f78c03a7cbf8c9af6c78b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/7425e155cf22cdd2b4dd3458a7da4cf6c0201562", - "reference": "7425e155cf22cdd2b4dd3458a7da4cf6c0201562", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/b487b11f75025fb1a91f78c03a7cbf8c9af6c78b", + "reference": "b487b11f75025fb1a91f78c03a7cbf8c9af6c78b", "shasum": "" }, "require": { + "composer/xdebug-handler": "^1.0", "ext-xml": "*", "pdepend/pdepend": "^2.5", "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.0", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.4", + "phpunit/phpunit": "^4.8.36 || ^5.7.27", "squizlabs/php_codesniffer": "^2.0" }, "bin": [ "src/bin/phpmd" ], - "type": "project", + "type": "library", "autoload": { "psr-0": { "PHPMD\\": "src/main/php" @@ -778,24 +2000,24 @@ "authors": [ { "name": "Manuel Pichler", + "role": "Project Founder", "email": "github@manuel-pichler.de", - "homepage": "https://github.com/manuelpichler", - "role": "Project Founder" - }, - { - "name": "Other contributors", - "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", - "role": "Contributors" + "homepage": "https://github.com/manuelpichler" }, { "name": "Marc Würth", + "role": "Project Maintainer", "email": "ravage@bluewin.ch", - "homepage": "https://github.com/ravage84", - "role": "Project Maintainer" + "homepage": "https://github.com/ravage84" + }, + { + "name": "Other contributors", + "role": "Contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors" } ], "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", - "homepage": "http://phpmd.org/", + "homepage": "https://phpmd.org/", "keywords": [ "mess detection", "mess detector", @@ -803,11 +2025,11 @@ "phpmd", "pmd" ], - "time": "2019-07-05T23:07:02+00:00" + "time": "2019-08-05T19:18:28+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", @@ -965,8 +2187,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", @@ -980,16 +2202,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "7f0f29702170e2786b2df813af970135765de6fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/7f0f29702170e2786b2df813af970135765de6fc", + "reference": "7f0f29702170e2786b2df813af970135765de6fc", "shasum": "" }, "require": { @@ -1016,8 +2238,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", @@ -1026,7 +2248,7 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "time": "2019-07-02T07:44:20+00:00" }, { "name": "phpunit/php-text-template", @@ -1058,8 +2280,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Simple template engine.", @@ -1071,16 +2293,16 @@ }, { "name": "phpunit/php-timer", - "version": "2.1.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + "reference": "37d2894f3650acccb6e57207e63eb9699c1a82a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/37d2894f3650acccb6e57207e63eb9699c1a82a6", + "reference": "37d2894f3650acccb6e57207e63eb9699c1a82a6", "shasum": "" }, "require": { @@ -1107,8 +2329,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Utility class for timing", @@ -1116,20 +2338,20 @@ "keywords": [ "timer" ], - "time": "2019-06-07T04:22:29+00:00" + "time": "2019-07-02T07:42:03+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c" + "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c", - "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", + "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", "shasum": "" }, "require": { @@ -1142,7 +2364,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -1165,20 +2387,20 @@ "keywords": [ "tokenizer" ], - "time": "2019-07-08T05:24:54+00:00" + "time": "2019-07-25T05:29:42+00:00" }, { "name": "phpunit/phpunit", - "version": "7.5.14", + "version": "7.5.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff" + "reference": "062dfa4b8836627848a471bb552ede3d5c4f2a8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2834789aeb9ac182ad69bfdf9ae91856a59945ff", - "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/062dfa4b8836627848a471bb552ede3d5c4f2a8f", + "reference": "062dfa4b8836627848a471bb552ede3d5c4f2a8f", "shasum": "" }, "require": { @@ -1238,8 +2460,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "The PHP Unit Testing framework.", @@ -1249,20 +2471,20 @@ "testing", "xunit" ], - "time": "2019-07-15T06:24:08+00:00" + "time": "2019-08-03T08:12:46+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "5e860800beea5ea4c8590df866338c09c20d3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e860800beea5ea4c8590df866338c09c20d3a48", + "reference": "5e860800beea5ea4c8590df866338c09c20d3a48", "shasum": "" }, "require": { @@ -1294,20 +2516,20 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "time": "2019-07-02T07:44:03+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "9a1267ac19ecd74163989bcb3e01c5c9587f9e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/9a1267ac19ecd74163989bcb3e01c5c9587f9e3b", + "reference": "9a1267ac19ecd74163989bcb3e01c5c9587f9e3b", "shasum": "" }, "require": { @@ -1358,20 +2580,20 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "time": "2019-07-02T07:45:15+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "d7e7810940c78f3343420f76adf92dc437b7a557" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/d7e7810940c78f3343420f76adf92dc437b7a557", + "reference": "d7e7810940c78f3343420f76adf92dc437b7a557", "shasum": "" }, "require": { @@ -1414,20 +2636,20 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "time": "2019-07-02T07:43:30+00:00" }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "1c91ab3fb351373cf86ead6006ea9daa8e4ce027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1c91ab3fb351373cf86ead6006ea9daa8e4ce027", + "reference": "1c91ab3fb351373cf86ead6006ea9daa8e4ce027", "shasum": "" }, "require": { @@ -1467,20 +2689,20 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "time": "2019-07-02T07:44:59+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe", + "reference": "97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe", "shasum": "" }, "require": { @@ -1534,7 +2756,7 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2019-07-02T07:44:27+00:00" }, { "name": "sebastian/global-state", @@ -1589,16 +2811,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "63e5a3e0881ebf28c9fbb2a2e12b77d373850c12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/63e5a3e0881ebf28c9fbb2a2e12b77d373850c12", + "reference": "63e5a3e0881ebf28c9fbb2a2e12b77d373850c12", "shasum": "" }, "require": { @@ -1632,20 +2854,20 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "time": "2019-07-02T07:43:46+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "3053ae3e6286fdf98769f18ec10894dbc6260a34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3053ae3e6286fdf98769f18ec10894dbc6260a34", + "reference": "3053ae3e6286fdf98769f18ec10894dbc6260a34", "shasum": "" }, "require": { @@ -1677,20 +2899,20 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "time": "2019-07-02T07:44:36+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "a58220ae18565f6004bbe15321efc4470bfe02fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/a58220ae18565f6004bbe15321efc4470bfe02fd", + "reference": "a58220ae18565f6004bbe15321efc4470bfe02fd", "shasum": "" }, "require": { @@ -1730,20 +2952,20 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "time": "2019-07-02T07:43:54+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "d67fc89d3107c396d161411b620619f3e7a7c270" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/d67fc89d3107c396d161411b620619f3e7a7c270", + "reference": "d67fc89d3107c396d161411b620619f3e7a7c270", "shasum": "" }, "require": { @@ -1772,7 +2994,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "time": "2019-07-02T07:42:50+00:00" }, { "name": "sebastian/version", @@ -1809,8 +3031,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", @@ -1859,16 +3081,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.4.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8" + "reference": "c3f7eed723f4b7b6faaf451373a54cf7f58103d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", - "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/c3f7eed723f4b7b6faaf451373a54cf7f58103d5", + "reference": "c3f7eed723f4b7b6faaf451373a54cf7f58103d5", "shasum": "" }, "require": { @@ -1906,252 +3128,7 @@ "phpcs", "standards" ], - "time": "2019-04-10T23:49:02+00:00" - }, - { - "name": "symfony/config", - "version": "v4.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "9198eea354be75794a7b1064de00d9ae9ae5090f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/9198eea354be75794a7b1064de00d9ae9ae5090f", - "reference": "9198eea354be75794a7b1064de00d9ae9ae5090f", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/finder": "<3.4" - }, - "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/messenger": "~4.1", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com", - "time": "2019-06-08T06:33:08+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v4.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "b851928be349c065197fdc0832f78d85139e3903" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/b851928be349c065197fdc0832f78d85139e3903", - "reference": "b851928be349c065197fdc0832f78d85139e3903", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/container": "^1.0", - "symfony/service-contracts": "^1.1.2" - }, - "conflict": { - "symfony/config": "<4.3", - "symfony/finder": "<3.4", - "symfony/proxy-manager-bridge": "<3.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0" - }, - "require-dev": { - "symfony/config": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DependencyInjection Component", - "homepage": "https://symfony.com", - "time": "2019-06-15T04:08:07+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v4.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "b9896d034463ad6fd2bf17e2bf9418caecd6313d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b9896d034463ad6fd2bf17e2bf9418caecd6313d", - "reference": "b9896d034463ad6fd2bf17e2bf9418caecd6313d", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-06-23T08:51:25+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-05T06:47:46+00:00" }, { "name": "theseer/tokenizer", @@ -2246,8 +3223,10 @@ } ], "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], + "minimum-stability": "dev", + "stability-flags": { + "symfony/serializer": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/config/services.yml b/config/services.yml new file mode 100644 index 0000000..90b914d --- /dev/null +++ b/config/services.yml @@ -0,0 +1,14 @@ +services: + _defaults: + autowire: true + public: false + + Isfett\PhpAnalyzer\: + resource: '../src' + + Isfett\PhpAnalyzer\DAO\: + resource: '../src/DAO' + autowire: false + + Isfett\PhpAnalyzer\Console\Application: + public: true diff --git a/docs/MostUsedConditions.md b/docs/MostUsedConditions.md new file mode 100644 index 0000000..c08156a --- /dev/null +++ b/docs/MostUsedConditions.md @@ -0,0 +1,259 @@ +# Most Used Conditions + +This command is inspired by Kent Beck's Medium-Article [Conditions Are Power-Law Distributed: An Example](https://medium.com/@kentbeck_7670/conditions-are-power-law-distributed-an-example-61fa4e0d3500). + +## Story +I wanted to inspect the conditions in our project, checking for magic numbers and magic strings, maybe inconsistency of equal (`==`) and identical (`===`) and different types (for example `!$user` instead of `null === $user`). + +By trying to get all `if`, `elseif` and `else if` conditions in different projects my command ended up like `grep -R --include='*.php' --exclude-dir=vendor --exclude-dir=.idea 'if' . | perl -nle 'print $2 if /. (else)*?\s?if\s?\((.*)\)(.*){/,' | sort | uniq -c | sort -rn | sed --expression="s/ [0-9]\+ /&;/g" > ~/conditions.csv`. I realized that just looking for if's and elseif's is not everything I'm interested in, so I wrote an PHP-Implementation which gives you much more control. You can also check ternaries, null-coalesce and bool-returning functions. + +While writing this command I found out that I did not only want to check the original conditions, but I wanted to know each part of an isset itself. Later I wanted to split conditions by `&&` and `||`. While adding more and more post-processors and checking the conditions I decided to remove some parts, like casts or assignments. And here we are now. + + + +### With this command you can: +- Declare which directory (recursive) you want to inspect +- Change the suffixes (default is just *.php) +- Exclude dirs/files/patterns +- Whitelist patterns (if you just want to check one/some file/s) +- Declare which conditions are interesting (like: only If or If and ElseIf, just ElseIf, just Ternary or Coalesce, see [Visitors](#visitors)) +- Post-process conditions (like split isset or by logical operators, remove assignments, see [Processors](#processors)) +- Activate Flip-Checking (maybe you want to check which conditions are not well written in yoda) +- Exclude conditions with less than n occurrences +- generate a csv (to plot graphs in [Excel](#create-a-log10-graph-in-excel) or gplot) + +### Syntax +```shell script +# cloned repository, maybe via composer global require +bin/php-analyzer most-used-conditions [--options] directory + +# phar +php-analyzer.phar most-used-conditions [--options] directory +``` + +If you omit the directory it will use the current working directory. You can use absolute or relative paths. + +### Options +- `--excludes` for excluding directories. Must be relative to source. Comma-separated list, for example: `--excludes=vendor,var` +- `--exclude-paths` for excluding paths. Comma-separated list, items could be a string or regexp, for example: `--exclude-paths=some/file.txt,some\/*\/dir` +- `--exclude-files` for excluding files. Comma-separated list, items could be a regexp, a [glob](https://www.php.net/glob) or string, for example: `--exclude-files=*Test.php` +- `--include-files` for just including specific files. Comma-separated list, items could be a regexp, a [glob](https://www.php.net/glob) or string. Useful if you just want to inspect one to n files, for example `--include-files=test.php` +- `--suffixes` for change the suffixes of files getting inspected. Default is `php`, but maybe you want to add phtml, with: `--suffixes=php,phtml` +- `--visitors` for which conditions should be added, see [Visitors](#visitors). Default is `If,ElseIf,Ternary`, if you just want to add conditions from if and elseif this would be `--visitors=If,ElseIf` +- `--processors` for post-process your conditions with different tasks, see [Processors](#processors). Default none, your conditions will be added like they are in your source-code. +- `--sort` for sorting-direction by the number of occurrences in the source code. Default is `asc`, change to desc with `--sort=desc` +- `--max-entries` for just showing the highest n entries. Default none, show all conditions. To just show the five highest `--max-entries=5` +- `--max-occurrences` for limiting the number of occurrences per condition. Default none, show all occurrences (relative filename + line number). If you just want to see "some" add `--max-occurrences=5` +- `--min-occurrences` for limiting the number of conditions and just show conditions with more occurrences in source-code than treshold. For example `--min-occurrences=5` just will ignore all conditions with four or less times in your source code +- `--with-flip-check` enables flip check, see [Flip-Check](#flip-check) +- `--without-occurrences` will hide all occurrences of the conditions and just print the table without any +- `--without-flags` will hide the "flags" like `flipped` when `--with-flip-check` is enabled +- `--without-affected-by-processor` will hide the information which processors post-processed a conditions +- `--with-csv` will export the result table to a csv (comma-separated). Add a filepath as value (absolute or relative) and make sure you have writing permission with the current user. Example `--with-csv=output.csv` will create a output.csv file in the current working directory +- `--csv-delimiter-semicolon` will change the delimiter from the csv to semicolon (;) + +### Visitors +For example we have some code like [this](examples/MostUsedConditions/visitors.php): +```php +`date('Y') === 2017` +- `Ternary`: This visitor will add all conditions on the left side of a ternary, here it will add `isset($_GET['page'])` +- `Coalesce`: Like Ternary, left side will be added, here `$_GET['page']` +- `BooleanReturn`: (experimental) Will add the return statements without `return` in methods that have :bool as return type (not docblock), here `null === $user` + +**Important:** This tool uses a php-parser, and ignores different code-styles. So it doesn't matter if you are using single-quotes or double-quotes, add different types or amount of whitespaces and so on. + +You can combine visitors, use all or just one with a comma-separated list, like `--visitors=If,ElseIf,Ternary` or `--visitors=Coalesce`. + +If you mistype the name of a visitor, you will raise an Exception including all possible names (case-sensitive). + + + +### Processors + +You can combine processors. Just add them to a comma-seperated list, like
`--processors=SplitIsset,SplitLogicalOperator`. The order of processors is important. In the current case it will start with the SplitIsset and then the SplitLogicalOperator. Your results might change if you change the order of the processors. + +You also can use none of the processors (default). + +If you mistype the name of a processor, you will raise an Exception including all possible names (case-sensitive). + + + +#### SplitIsset +This processor will split your isset conditions if there is more than one parameter inside. See [this](examples/MostUsedConditions/splitissetprocessor.php) source-code: +```php + + +#### SplitLogicalOperator +This processor will split your conditions by logical operators, to be exact: `&&`, `||`, `and` and `or`. If there is a `!` outside, this will be added to both parts of the condition. See [this](examples/MostUsedConditions/splitlogicaloperatorprocessor.php) source-code: +```php + + +#### NegateBooleanNot +This processor will negate all comparison operators when it's negated from outside. See [this](examples/MostUsedConditions/negatebooleannotprocessor.php) source-code: +```php + 13`. This processor is very nice in combination with [SplitLogicalOperator](#splitlogicaloperator) from above, but make sure to add this processor in the list after the SplitLogicalOperator + +`php bin/php-analyzer most-used-conditions --without-affected-by-processors --visitors=If --processors=NegateBooleanNot --include-files=negatebooleannotprocessor.php` + + + +#### RemoveAssignment +This processor will remove assignments in your conditions. See [this](examples/MostUsedConditions/removeassignmentprocessor.php) source-code: +```php +getUser()) { + // do something +} + +if ($user = $this->getUser()) { + // do something else +} +``` +You want to know that the condition from $this->getUser() is used twice, but normally it would be counted as one plus one. This processor will remove `$user = ` from the second condition and will count 2 for `$this->getUser()`. + +`php bin/php-analyzer most-used-conditions --without-affected-by-processors --visitors=If --processors=RemoveAssignment --include-files=removeassignmentprocessor.php` + + + +#### RemoveDuplicateBooleanNot +This processor removes duplicate negations. So if you have a complex condition, and split it off, it could be possible for the printed condition to be something like `!!$user`. You can achieve this with [this](examples/MostUsedConditions/removeduplicatebooleannotprocessor.php) source-code and the processors [SplitLogicalOperator](#splitlogicaloperator) and [NegateBooleanNot](#negatebooleannot) together: +```php + + +#### RemoveCast +This processor will any (cast)-casts (like `(int)`). I saw this in some legacy applications. See [this](examples/MostUsedConditions/removecastprocessor.php) source-code: +```php + + +### Flip-Check +- with `--with-flip-check` the command will try to swap both sides of `==`, `!=`, `===` and `!==` and check if it already exists. If yes it will mark it with a `flipped`-flag. See [this](examples/MostUsedConditions/flipcheck.php) source-code: +```php + + +### Create a log(10) graph in Excel +- Open the csv and save it to xlsx +- Select rows A and B and insert a `Scatter (XY)`-Graph + +Your graph should look similar to: + + +- Left-Click on x-axis to select the axis, then right-click and select `format axis`. +- Select in the sidebar `logarithmic scale` and select `10` +- Repeat for y-axis + +Your log(10) graph should look similar to: + diff --git a/docs/examples/MostUsedConditions/flipcheck.php b/docs/examples/MostUsedConditions/flipcheck.php new file mode 100644 index 0000000..0087407 --- /dev/null +++ b/docs/examples/MostUsedConditions/flipcheck.php @@ -0,0 +1,9 @@ +getUsername()); +} + +if( date('Y') === 2019) { + echo '2019!!!'; +} elseif ($user) { + echo 'x)'; +} + +echo date('Y') === 2019 ? 'x' :'y'; + +$page = $_GET['page'] ?? 1; diff --git a/docs/examples/MostUsedConditions/negatebooleannotprocessor.php b/docs/examples/MostUsedConditions/negatebooleannotprocessor.php new file mode 100644 index 0000000..8c0ca8a --- /dev/null +++ b/docs/examples/MostUsedConditions/negatebooleannotprocessor.php @@ -0,0 +1,14 @@ +getUser()) { + // do something +} + +if ($user = $this->getUser()) { + // do something else +} diff --git a/docs/examples/MostUsedConditions/removecastprocessor.php b/docs/examples/MostUsedConditions/removecastprocessor.php new file mode 100644 index 0000000..d6b83e1 --- /dev/null +++ b/docs/examples/MostUsedConditions/removecastprocessor.php @@ -0,0 +1,6 @@ + - - tests/ + + ./tests/Unit + + + ./tests/Integration @@ -16,14 +22,18 @@ src/ + src/Console/ + src/DAO/ + src/DependencyInjection/ vendor/ + src/Kernel.php - + diff --git a/src/Builder/ConditionListBuilder.php b/src/Builder/ConditionListBuilder.php new file mode 100644 index 0000000..04707d8 --- /dev/null +++ b/src/Builder/ConditionListBuilder.php @@ -0,0 +1,40 @@ +isFlipCheckingAware) { + return new FlipChecking(); + } + + return new ConditionList(); + } + + /** + * @param bool $flipCheckingAwareness + * + * @return ConditionListBuilderInterface + */ + public function setIsFlipCheckingAware(bool $flipCheckingAwareness): ConditionListBuilderInterface + { + $this->isFlipCheckingAware = $flipCheckingAwareness; + + return $this; + } +} diff --git a/src/Builder/ConditionListBuilderInterface.php b/src/Builder/ConditionListBuilderInterface.php new file mode 100644 index 0000000..f60d80a --- /dev/null +++ b/src/Builder/ConditionListBuilderInterface.php @@ -0,0 +1,24 @@ +directories, + $this->includeFiles, + $this->excludes, + $this->excludePaths, + $this->excludeFiles, + $this->suffixes + ); + } + + /** + * @param array $directories + * + * @return FinderBuilderInterface + */ + public function setDirectories(array $directories): FinderBuilderInterface + { + $this->directories = $directories; + + return $this; + } + + /** + * @param array $includeFiles + * + * @return FinderBuilderInterface + */ + public function setIncludeFiles(array $includeFiles): FinderBuilderInterface + { + $this->includeFiles = $includeFiles; + + return $this; + } + + /** + * @param array $excludes + * + * @return FinderBuilderInterface + */ + public function setExcludes(array $excludes): FinderBuilderInterface + { + $this->excludes = $excludes; + + return $this; + } + + /** + * @param array $excludePaths + * + * @return FinderBuilderInterface + */ + public function setExcludePaths(array $excludePaths): FinderBuilderInterface + { + $this->excludePaths = $excludePaths; + + return $this; + } + + /** + * @param array $excludeFiles + * + * @return FinderBuilderInterface + */ + public function setExcludeFiles(array $excludeFiles): FinderBuilderInterface + { + $this->excludeFiles = $excludeFiles; + + return $this; + } + + /** + * @param array $suffixes + * + * @return FinderBuilderInterface + */ + public function setSuffixes(array $suffixes): FinderBuilderInterface + { + $this->suffixes = $suffixes; + + return $this; + } +} diff --git a/src/Builder/FinderBuilderInterface.php b/src/Builder/FinderBuilderInterface.php new file mode 100644 index 0000000..1be7dfb --- /dev/null +++ b/src/Builder/FinderBuilderInterface.php @@ -0,0 +1,59 @@ + + */ + public function getProcessors(): ArrayCollection + { + $visitors = new ArrayCollection(); + + foreach ($this->names as $name) { + $classname = sprintf('Isfett\\PhpAnalyzer\\Node\\Processor\\%sProcessor', $name); + + if (!class_exists($classname)) { + throw new InvalidProcessorNameException($name); + } + + $visitors->add(new $classname()); + } + + return $visitors; + } + + /** + * @param string $names + * + * @return ProcessorBuilderInterface + */ + public function setNames(string $names): ProcessorBuilderInterface + { + if ('' === $names) { + $this->names = []; + } else { + $this->names = explode(',', str_replace(', ', ',', $names)); + } + + return $this; + } +} diff --git a/src/Builder/ProcessorBuilderInterface.php b/src/Builder/ProcessorBuilderInterface.php new file mode 100644 index 0000000..95fb57f --- /dev/null +++ b/src/Builder/ProcessorBuilderInterface.php @@ -0,0 +1,25 @@ + + */ + public function getProcessors(): ArrayCollection; + + /** + * @param string $names + * + * @return self + */ + public function setNames(string $names): self; +} diff --git a/src/Builder/SortConfigurationBuilder.php b/src/Builder/SortConfigurationBuilder.php new file mode 100644 index 0000000..3ddfcb5 --- /dev/null +++ b/src/Builder/SortConfigurationBuilder.php @@ -0,0 +1,91 @@ +sortFields = new ArrayCollection(); + } + + /** + * @return Sort + * @throws InvalidSortConfigurationException + */ + public function getSortConfiguration(): Sort + { + if (0 === $this->sortFields->count()) { + throw new InvalidSortConfigurationException('You need to add at least one sort field'); + } + + return new Sort($this->sortFields, $this->firstResult, $this->maxResults); + } + + /** + * @param int|null $maxResults + * + * @return SortConfigurationBuilderInterface + */ + public function setMaxResults(?int $maxResults = null): SortConfigurationBuilderInterface + { + $this->maxResults = $maxResults; + + return $this; + } + + /** + * @param int|null $firstResult + * + * @return SortConfigurationBuilderInterface + */ + public function setFirstResult(?int $firstResult = null): SortConfigurationBuilderInterface + { + $this->firstResult = $firstResult; + + return $this; + } + + /** + * @param string $field + * @param string $direction + * + * @return SortConfigurationBuilderInterface + * @throws InvalidSortArgumentException + */ + public function addSortField(string $field, string $direction): SortConfigurationBuilderInterface + { + $direction = strtoupper($direction); + if (Criteria::ASC !== $direction && Criteria::DESC !== $direction) { + throw new InvalidSortArgumentException($direction); + } + + $sortField = new SortField($field, $direction); + $this->sortFields->add($sortField); + + return $this; + } +} diff --git a/src/Builder/SortConfigurationBuilderInterface.php b/src/Builder/SortConfigurationBuilderInterface.php new file mode 100644 index 0000000..9d60cff --- /dev/null +++ b/src/Builder/SortConfigurationBuilderInterface.php @@ -0,0 +1,39 @@ + + */ + public function getVisitors(): ArrayCollection + { + $visitors = new ArrayCollection(); + + foreach ($this->names as $name) { + $classname = sprintf('Isfett\\PhpAnalyzer\\Node\\Visitor\\%sConditionVisitor', $name); + + if (!class_exists($classname)) { + throw new InvalidVisitorNameException($name); + } + + $visitors->add(new $classname()); + } + + return $visitors; + } + + /** + * @param string $names + * + * @return VisitorBuilderInterface + */ + public function setNames(string $names): VisitorBuilderInterface + { + if ('' === $names) { + $this->names = []; + } else { + $this->names = explode(',', str_replace(', ', ',', $names)); + } + + return $this; + } +} diff --git a/src/Builder/VisitorBuilderInterface.php b/src/Builder/VisitorBuilderInterface.php new file mode 100644 index 0000000..8a48e8a --- /dev/null +++ b/src/Builder/VisitorBuilderInterface.php @@ -0,0 +1,25 @@ + + */ + public function getVisitors(): ArrayCollection; + + /** + * @param string $names + * + * @return self + */ + public function setNames(string $names): self; +} diff --git a/src/Console/Application.php b/src/Console/Application.php index a5fc821..ca6a593 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -4,6 +4,8 @@ namespace Isfett\PhpAnalyzer\Console; use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -45,6 +47,7 @@ public function __construct() */ public function doRun(InputInterface $input, OutputInterface $output): int { + $this->initStyles($output); $this->printApplicationInfoWhenNotInQuietMode($input, $output); if ($this->checkParameterOptionVersion($input)) { @@ -58,16 +61,6 @@ public function doRun(InputInterface $input, OutputInterface $output): int return parent::doRun($input, $output); } - /** - * @return array - */ - protected function getDefaultCommands(): array - { - $defaultCommands = parent::getDefaultCommands(); - - return $defaultCommands; - } - /** * @param InputInterface $input * @param OutputInterface $output @@ -95,6 +88,47 @@ private function printApplicationInfoWhenNotInQuietMode(InputInterface $input, O */ private function checkParameterOptionVersion(InputInterface $input): bool { - return $input->hasParameterOption('--version') || $input->hasParameterOption('-v'); + return $input->hasParameterOption('--version'); + } + + /** + * @param OutputInterface $output + */ + private function initStyles(OutputInterface $output): void + { + $outputFormatter = $output->getFormatter(); + $outputFormatter->setStyle( + 'command-start', + new OutputFormatterStyle('red', 'black', ['bold']) + ); + $outputFormatter->setStyle( + 'focus', + new OutputFormatterStyle('cyan', 'black', ['bold']) + ); + $outputFormatter->setStyle( + 'flag', + new OutputFormatterStyle('yellow', 'black', ['bold']) + ); + $outputFormatter->setStyle( + 'special-info', + new OutputFormatterStyle('magenta', 'black') + ); + + ProgressBar::setFormatDefinition( + 'messageOnly', + '%message%' + ); + ProgressBar::setFormatDefinition( + 'messageDuration', + '%message% (took %elapsed:6s%)' + ); + ProgressBar::setFormatDefinition( + 'customFinder', + '%elapsed:6s% | %message% -> %filename%' + ); + ProgressBar::setFormatDefinition( + 'customBar', + '%current%/%max% (%percent:2s%%) [%bar%] %elapsed:6s% -> %message%' + ); } -} \ No newline at end of file +} diff --git a/src/Console/Command/MostUsedConditionsCommand.php b/src/Console/Command/MostUsedConditionsCommand.php new file mode 100644 index 0000000..251e608 --- /dev/null +++ b/src/Console/Command/MostUsedConditionsCommand.php @@ -0,0 +1,656 @@ +finderBuilder = $finderBuilder; + $this->conditionListBuilder = $conditionListBuilder; + $this->visitorBuilder = $visitorBuilder; + $this->sortConfigurationBuilder = $sortConfigurationBuilder; + $this->processorBuilder = $processorBuilder; + $this->nodeRepresentationService = $nodeRepresentationService; + $this->processorRunner = $processorRunner; + $this->sortService = $sortService; + + parent::__construct(self::NAME); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + */ + public function run(InputInterface $input, OutputInterface $output): int + { + $output->write(['Starting most-used-conditions command'], ['']); + + $directory = realpath($input->getArgument('directory')); + if (false === $directory) { + throw new \RuntimeException(sprintf( + 'Directory %s does not exist', + $input->getArgument('directory') + )); + } + + $finderProgressBar = $this->createProgressBar($output, 'customFinder'); + + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + + $finder = $this->finderBuilder + ->setDirectories([$directory]) + ->setIncludeFiles($this->getArrayOption('include-files', $input)) + ->setExcludes($this->getArrayOption('excludes', $input)) + ->setExcludePaths($this->getArrayOption('exclude-paths', $input)) + ->setExcludeFiles($this->getArrayOption('exclude-files', $input)) + ->setSuffixes($this->getArrayOption('suffixes', $input)) + ->getFinder(); + + $files = $this->processFinder($finder, $finderProgressBar); + + $this->finishProgressBar($finderProgressBar, $output); + + if (0 === count($files)) { + $output->writeln('No files found'); + + return Application::EXIT_CODE_FAILURE; + } + + $traverser = new Traverser(); + $visitors = $this->visitorBuilder->setNames($input->getOption('visitors'))->getVisitors(); + foreach ($visitors as $visitor) { + $output->writeln('Adding '.$this->getClassnameWithoutNamespace(get_class($visitor)).' Visitor'); + $traverser->addVisitor($visitor); + } + + $traverserProgressBar = $this->createProgressBar($output, 'customBar', 100); + $traverserProgressBar->setMaxSteps(count($files)); + + foreach ($files as $file) { + try { + $ast = $parser->parse($file->getContents()); + } catch (Error $exception) { + continue; + } finally { + $traverserProgressBar->advance(); + } + $traverser->setFile($file); + $traverser->traverse($ast); + $traverserProgressBar->setMessage(sprintf( + 'Visitors are checking for conditions in files. Condition count: %d', + $traverser->getNodeOccurrencesCount() + )); + } + + $traverserProgressBar->setMessage(sprintf( + 'Visitors checked conditions in %d files. Condition count: %d', + count($files), + $traverser->getNodeOccurrencesCount() + )); + $this->finishProgressBar($traverserProgressBar, $output); + + $occurrenceList = new OccurrenceList(); + + /** @var AbstractVisitor $visitor */ + foreach ($visitors as $visitor) { + /** @var Occurrence $occurrence */ + foreach ($visitor->getNodeOccurrenceList()->getOccurrences() as $occurrence) { + $occurrenceList->addOccurrence($occurrence); + } + } + + $processors = $this->processorBuilder->setNames($input->getOption('processors'))->getProcessors(); + + if (count($processors)) { + foreach ($processors as $processor) { + $output->writeln('Adding '.$this->getClassnameWithoutNamespace(get_class($processor)).' Processor'); + $this->processorRunner->addProcessor($processor); + } + + $processorsProgressBar = $this->createProgressBar($output, 'customBar', 100); + + foreach ($processorsProgressBar->iterate( + $this->processorRunner->process($occurrenceList), + count($processors) + ) as $processorsDone) { + $processorsProgressBar->setMessage(sprintf( + 'Processor %d is processing conditions. Condition count: %d', + $processorsDone, + count($occurrenceList->getOccurrences()) + )); + } + + $processorsProgressBar->setMessage(sprintf( + 'Processors processed conditions. Condition count: %d', + count($occurrenceList->getOccurrences()) + )); + $this->finishProgressBar($processorsProgressBar, $output); + } + + $flipChecking = $input->getOption('with-flip-check'); + $conditionListProgressBar = $this->createProgressBar($output, 'customBar', 100); + $conditionListProgressBar->setMaxSteps(count($occurrenceList->getOccurrences())); + $conditionListProgressBar->setMessage(sprintf( + 'Create ConditionList (print ast nodes). Flip-Check: %s', + $flipChecking ? 'active' : 'inactive' + )); + + /** @var ConditionList|FlipChecking $conditionList */ + $conditionList = $this->conditionListBuilder + ->setIsFlipCheckingAware($flipChecking) + ->getConditionList(); + + $flippedConditionCounter = 0; + /** @var Occurrence $occurrence */ + foreach ($occurrenceList->getOccurrences() as $occurrence) { + $representation = $this->nodeRepresentationService->representationForNode($occurrence->getNode()); + $condition = new Condition($representation, $occurrence); + $conditionList->addCondition($condition); + + $conditionListProgressBar->advance(); + if ($flipChecking && $condition->getCondition() !== $representation) { + $flippedConditionCounter++; + $conditionListProgressBar->setMessage( + sprintf( + 'Create ConditionList (print ast nodes). Flip-Check: %s. Flipped conditions: %d', + $flipChecking ? 'active' : 'inactive', + $flippedConditionCounter + ) + ); + } + } + + $this->finishProgressBar($conditionListProgressBar, $output); + + $rawConditions = $conditionList->getConditions(); + $countedConditionsList = new Countable(); + + $countedListProgressBar = $this->createProgressBar($output, 'customBar', 100); + $countedListProgressBar->setMaxSteps(count($rawConditions)); + $countedListProgressBar->setMessage('Check for multiple conditions'); + + foreach ($rawConditions as $condition) { + $countedConditionsList->addCondition($condition); + + $countedListProgressBar->advance(); + $countedListProgressBar->setMessage(sprintf( + 'Check for multiple conditions. Unique conditions: %d', + count($countedConditionsList->getCountedConditions()) + )); + } + + $this->finishProgressBar($countedListProgressBar, $output); + + $maxEntries = $this->getMaxEntries($input); + $sortDirection = $this->getSort($input); + + $countedConditions = $countedConditionsList->getCountedConditions(); + + $output->writeln(sprintf( + 'Sort Conditions by number of occurrences %s.', + $sortDirection + )); + + $calculatedFirstResult = null; + $calculatedMaxResults = null; + if (null !== $maxEntries) { + if ('desc' === strtolower($sortDirection)) { + $calculatedMaxResults = $maxEntries; + } else { + $calculatedFirstResult = $countedConditions->count() - $maxEntries; + } + } + + $sortConfiguration = $this->sortConfigurationBuilder + ->setMaxResults($calculatedMaxResults) + ->setFirstResult($calculatedFirstResult) + ->addSortField('count', $sortDirection) + ->addSortField('condition', 'ASC') + ->getSortConfiguration(); + + $sortedConditions = $this->sortService->sortArrayCollection($countedConditions, $sortConfiguration); + + $hideOccurrences = $input->getOption('without-occurrences'); + $hideFlags = $input->getOption('without-flags'); + $hideAffectedByProcessors = $input->getOption('without-affected-by-processors'); + $maximumOccurrences = $input->getOption('max-occurrences'); + $minOccurrences = $input->getOption('min-occurrences'); + if (null !== $minOccurrences) { + $minOccurrences = (int) $minOccurrences; + $output->writeln(sprintf( + 'Just showing conditions with at least %d occurrences', + $minOccurrences + )); + } + if (null !== $maxEntries) { + $output->writeln(sprintf( + 'Just showing maximum %d conditions.', + $maxEntries + )); + } + if (null !== $maximumOccurrences) { + $maximumOccurrences = (int) $maximumOccurrences; + } + + $csvExport = $input->getOption('with-csv'); + $csvExportData = []; + + $table = new Table($output); + $table->setColumnMaxWidth(0, 100); + $table->setHeaders([ + 'Condition', + 'Count', + ]); + + $conditionCounter = 0; + /** @var CountedCondition $countedCondition */ + foreach ($sortedConditions as $countedCondition) { + $conditionCounter++; + if (null !== $minOccurrences && $minOccurrences > $countedCondition->getCount()) { + continue; + } + $table->addRow([ + sprintf('%s', $countedCondition->getCondition()), + $countedCondition->getCount(), + ]); + if ($csvExport) { + $csvExportData[] = [$countedCondition->getCondition(), $countedCondition->getCount()]; + } + if (!$hideOccurrences) { + /** @var Occurrence $occurrence */ + $counter = 1; + foreach ($countedCondition->getOccurrences() as $occurrence) { + if (null !== $maximumOccurrences && $counter > $maximumOccurrences) { + break; + } + $line = $occurrence->getNode()->getStartLine(); + if ($occurrence->getNode()->getStartLine() !== $occurrence->getNode()->getEndLine()) { + $line .= sprintf('-%s', $occurrence->getNode()->getEndLine()); + } + $flags = []; + if ($occurrence->isFlipped()) { + $flags[] = 'flipped'; + } + $affectedByProcessors = $occurrence->getAffectedByProcessors(); + + $showFlags = !$hideFlags && count($flags); + $showAffectedByProcessors = !$hideAffectedByProcessors && count($affectedByProcessors); + + $table->addRow([ + sprintf( + '%s %s%s', + sprintf( + '%s:%s', + $occurrence->getFile()->getPathname(), + $occurrence->getFile()->getRelativePathname(), + $line + ), + $showFlags ? '(' . implode(', ', $flags) . ') ' : '', + $showAffectedByProcessors ? '(' . implode(', ', $affectedByProcessors) . ')' : '' + ), + '', + ]); + $counter++; + } + } + if ($conditionCounter < $maxEntries || + (null === $maxEntries && $conditionCounter < $countedConditions->count()) + ) { + $table->addRow([new TableSeparator(), new TableSeparator()]); + } + } + + $table->render(); + + $output->write(PHP_EOL); + + if ($csvExport) { + $serializer = new Serializer([], [new CsvEncoder()]); + $csvDelimiter = $input->getOption('csv-delimiter-semicolon') ? ';' : ','; + $csvExportOptions = [ + CsvEncoder::NO_HEADERS_KEY => true, + CsvEncoder::DELIMITER_KEY => $csvDelimiter, + ]; + $csvDelimiterReplace = ',' === $csvDelimiter ? '[comma]' : '[semicolon]'; + array_walk($csvExportData, static function (&$data) use ($csvDelimiter, $csvDelimiterReplace) { + $data[0] = str_replace($csvDelimiter, $csvDelimiterReplace, $data[0]); + + return $data; + }); + file_put_contents($csvExport, $serializer->encode($csvExportData, 'csv', $csvExportOptions)); + $output->writeln(sprintf( + 'Exported conditions with delimiter "%s" to %s', + $csvDelimiter, + realpath($csvExport) + ) . PHP_EOL); + } + + return Application::EXIT_CODE_SUCCESS; + } + + /** + * @return void + */ + protected function configure(): void + { + $this + ->setName(self::NAME) + ->setHelp('Find out your most used conditions') + ->addArgument('username', InputArgument::REQUIRED, 'The username of the user.') + ->setDefinition( + new InputDefinition([ + new InputArgument( + 'directory', + InputArgument::OPTIONAL, + 'path to directory which should be checked', + getcwd() + ), + ]) + ) + ->addOption( + 'excludes', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of directories-names which should be excluded (must be relative to source)', + 'vendor' + ) + ->addOption( + 'exclude-paths', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of directories-paths which should be excluded (must be relative to source)', + '' + ) + ->addOption( + 'exclude-files', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of files which should be excluded (must be relative to source)', + '' + ) + ->addOption( + 'include-files', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of files which should be included (must be relative to source)', + '' + ) + ->addOption( + 'suffixes', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of valid source code filename extensions', + 'php' + ) + ->addOption( + 'visitors', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of visitors which should check the source code to + find conditions (on wrong input you can see a list of possible visitors)', + 'If,ElseIf,Ternary' + ) + ->addOption( + 'processors', + null, + InputOption::VALUE_REQUIRED, + 'Comma-separated string of processors which should transform the conditions + (on wrong input you can see a list of possible processor)', + '' + ) + ->addOption( + 'sort', + null, + InputOption::VALUE_REQUIRED, + 'sort direction of conditions, desc or asc', + 'asc' + ) + ->addOption( + 'max-entries', + null, + InputOption::VALUE_REQUIRED, + 'maximum entries' + ) + ->addOption( + 'max-occurrences', + null, + InputOption::VALUE_REQUIRED, + 'maximum occurrences' + ) + ->addOption( + 'min-occurrences', + null, + InputOption::VALUE_REQUIRED, + 'minimum occurrences' + ) + ->addOption( + 'with-flip-check', + null, + InputOption::VALUE_NONE, + 'flip checking conditions' + ) + ->addOption( + 'without-occurrences', + null, + InputOption::VALUE_NONE, + 'hide occurrences' + ) + ->addOption( + 'without-flags', + null, + InputOption::VALUE_NONE, + 'hide flags (like flipped)' + ) + ->addOption( + 'without-affected-by-processors', + null, + InputOption::VALUE_NONE, + 'hide processors who affected a condition' + ) + ->addOption( + 'with-csv', + null, + InputOption::VALUE_REQUIRED, + 'enable csv export to filepath', + null + ) + ->addOption( + 'csv-delimiter-semicolon', + null, + InputOption::VALUE_NONE, + 'change the csv delimiter to ;' + ); + } + + /** + * @param string $name + * @param InputInterface $input + * + * @return array + */ + private function getArrayOption(string $name, InputInterface $input): array + { + if ('' === $inputOption = $input->getOption($name)) { + return []; + } + + return explode(',', $inputOption); + } + + /** + * @param string $classname + * + * @return string + */ + private function getClassnameWithoutNamespace(string $classname): string + { + $classWithNamespaces = explode('\\', $classname); + + return end($classWithNamespaces); + } + + /** + * @param OutputInterface $output + * @param string $format + * @param int $max + * + * @return ProgressBar + */ + private function createProgressBar(OutputInterface $output, string $format, int $max = 0): ProgressBar + { + $progressBar = new ProgressBar($output, $max); + $progressBar->setFormat($format); + $progressBar->start(); + + return $progressBar; + } + + /** + * @param ProgressBar $progressBar + * @param OutputInterface $output + * + * @return void + */ + private function finishProgressBar(ProgressBar $progressBar, OutputInterface $output): void + { + $progressBar->setFormat('messageDuration'); + $progressBar->finish(); + $output->writeln(''); + } + + /** + * @param InputInterface $input + * + * @return string + */ + private function getSort(InputInterface $input): string + { + return $input->getOption('sort'); + } + + /** + * @param Finder $finder + * @param ProgressBar $progressBar + * + * @return array + */ + private function processFinder(Finder $finder, ProgressBar $progressBar): array + { + $files = []; + $progressBar->setMessage('Looking for files'); + + /** @var SplFileInfo $file */ + foreach ($progressBar->iterate($finder->getIterator()) as $file) { + $files[] = $file; + $progressBar->setMessage(sprintf('(%s)', $file->getRelativePathname()), 'filename'); + $progressBar->setMessage(sprintf('Looking for files. File count: %d', count($files))); + } + + return $files; + } + + /** + * @param InputInterface $input + * + * @return int|null + */ + private function getMaxEntries(InputInterface $input): ?int + { + $maximumEntries = $input->getOption('max-entries'); + if (null !== $maximumEntries) { + $maximumEntries = (int) $maximumEntries; + } + + return $maximumEntries; + } +} diff --git a/src/DAO/Condition.php b/src/DAO/Condition.php new file mode 100644 index 0000000..dbb35fe --- /dev/null +++ b/src/DAO/Condition.php @@ -0,0 +1,52 @@ +condition = $condition; + $this->occurrence = $occurrence; + } + + /** + * @return string + */ + public function getCondition(): string + { + return $this->condition; + } + + /** + * @param string $condition + */ + public function setCondition(string $condition): void + { + $this->condition = $condition; + } + + /** + * @return Occurrence + */ + public function getOccurrence(): Occurrence + { + return $this->occurrence; + } +} diff --git a/src/DAO/ConditionList.php b/src/DAO/ConditionList.php new file mode 100644 index 0000000..462c160 --- /dev/null +++ b/src/DAO/ConditionList.php @@ -0,0 +1,41 @@ + */ + private $conditions; + + /** + * ConditionList constructor. + */ + public function __construct() + { + $this->conditions = new ArrayCollection(); + } + + /** + * @return ArrayCollection + */ + public function getConditions(): ArrayCollection + { + return $this->conditions; + } + + /** + * @param Condition $condition + * + * @return void + */ + public function addCondition(Condition $condition): void + { + $this->conditions->add($condition); + } +} diff --git a/src/DAO/Configuration/Sort.php b/src/DAO/Configuration/Sort.php new file mode 100644 index 0000000..ee34852 --- /dev/null +++ b/src/DAO/Configuration/Sort.php @@ -0,0 +1,59 @@ + */ + private $fields; + + /** @var int|null */ + private $firstResult; + + /** @var int|null */ + private $maxResults; + + /** + * Sort constructor. + * + * @param Collection $fields + * @param int|null $firstResult + * @param int|null $maxResults + */ + public function __construct(Collection $fields, ?int $firstResult = null, ?int $maxResults = null) + { + $this->fields = $fields; + $this->firstResult = $firstResult; + $this->maxResults = $maxResults; + } + + /** + * @return Collection + */ + public function getFields(): Collection + { + return $this->fields; + } + + /** + * @return int|null + */ + public function getMaxResults(): ?int + { + return $this->maxResults; + } + + /** + * @return int|null + */ + public function getFirstResult(): ?int + { + return $this->firstResult; + } +} diff --git a/src/DAO/Configuration/SortField.php b/src/DAO/Configuration/SortField.php new file mode 100644 index 0000000..905eaef --- /dev/null +++ b/src/DAO/Configuration/SortField.php @@ -0,0 +1,44 @@ +direction = $direction; + $this->field = $field; + } + + /** + * @return string + */ + public function getField(): string + { + return $this->field; + } + + /** + * @return string + */ + public function getDirection(): string + { + return $this->direction; + } +} diff --git a/src/DAO/CountedCondition.php b/src/DAO/CountedCondition.php new file mode 100644 index 0000000..dd1f8b7 --- /dev/null +++ b/src/DAO/CountedCondition.php @@ -0,0 +1,62 @@ + */ + private $occurrences = []; + + /** + * CountedCondition constructor. + * + * @param string $condition + * @param Occurrence $occurrence + */ + public function __construct(string $condition, Occurrence $occurrence) + { + $this->condition = $condition; + $this->addOccurrence($occurrence); + } + + /** + * @return int + */ + public function getCount(): int + { + return count($this->occurrences); + } + + /** + * @return string + */ + public function getCondition(): string + { + return $this->condition; + } + + /** + * @return array + */ + public function getOccurrences(): array + { + return $this->occurrences; + } + + /** + * @param Occurrence $occurrence + * + * @return void + */ + public function addOccurrence(Occurrence $occurrence): void + { + $this->occurrences[] = $occurrence; + } +} diff --git a/src/DAO/Occurrence.php b/src/DAO/Occurrence.php new file mode 100644 index 0000000..700a01a --- /dev/null +++ b/src/DAO/Occurrence.php @@ -0,0 +1,97 @@ +node = $node; + $this->file = $file; + } + + /** + * @return Node + */ + public function getNode(): Node + { + return $this->node; + } + + /** + * @param Node $node + */ + public function setNode(Node $node): void + { + $this->node = $node; + } + + /** + * @return SplFileInfo + */ + public function getFile(): SplFileInfo + { + return $this->file; + } + + /** + * @return bool + */ + public function isFlipped(): bool + { + return $this->isFlipped; + } + + /** + * @param bool $isFlipped + */ + public function setIsFlipped(bool $isFlipped): void + { + $this->isFlipped = $isFlipped; + } + + /** + * @return array + */ + public function getAffectedByProcessors(): array + { + return $this->affectedByProcessors; + } + + /** + * @param string $processorName + * + * @return void + */ + public function addAffectedByProcessor(string $processorName): void + { + if (!in_array($processorName, $this->affectedByProcessors, true)) { + $this->affectedByProcessors[] = $processorName; + } + } +} diff --git a/src/DAO/OccurrenceList.php b/src/DAO/OccurrenceList.php new file mode 100644 index 0000000..a1807e1 --- /dev/null +++ b/src/DAO/OccurrenceList.php @@ -0,0 +1,59 @@ + */ + private $occurrences; + + /** + * OccurrenceList constructor. + */ + public function __construct() + { + $this->occurrences = new ArrayCollection(); + } + + /** + * @return int + */ + public function count(): int + { + return $this->occurrences->count(); + } + + /** + * @return ArrayCollection + */ + public function getOccurrences(): ArrayCollection + { + return $this->occurrences; + } + + /** + * @param Occurrence $occurrence + * + * @return void + */ + public function addOccurrence(Occurrence $occurrence): void + { + $this->occurrences->add($occurrence); + } + + /** + * @param Occurrence $occurrence + * + * @return void + */ + public function removeOccurrence(Occurrence $occurrence): void + { + $this->occurrences->removeElement($occurrence); + } +} diff --git a/src/DependencyInjection/Compiler.php b/src/DependencyInjection/Compiler.php new file mode 100644 index 0000000..0c13fe9 --- /dev/null +++ b/src/DependencyInjection/Compiler.php @@ -0,0 +1,34 @@ +findDefinition(Application::class); + + foreach ($containerBuilder->getDefinitions() as $definition) { + if (! is_a($definition->getClass(), Command::class, true)) { + continue; + } + + $applicationDefinition->addMethodCall('add', [new Reference($definition->getClass())]); + } + } +} diff --git a/src/Exception/InvalidProcessorNameException.php b/src/Exception/InvalidProcessorNameException.php new file mode 100644 index 0000000..b712c7e --- /dev/null +++ b/src/Exception/InvalidProcessorNameException.php @@ -0,0 +1,43 @@ +getPossibleProcessorNames(); + parent::__construct(sprintf( + 'Processor with name %s does not exist. Possible processor-names are: %s', + $processorName, + implode(', ', $possibleProcessorNames) + ), 0, $previous); + } + + /** + * @return array + */ + private function getPossibleProcessorNames(): array + { + $finder = new Finder([dirname(__DIR__) . '/Node/Processor'], [], [], [], ['ProcessorInterface.php'], []); + $finder->sortByName(); + $possibleVisitorNames = []; + foreach ($finder->getIterator() as $file) { + $possibleVisitorNames[] = str_replace('Processor.php', '', $file->getRelativePathname()); + } + + return $possibleVisitorNames; + } +} diff --git a/src/Exception/InvalidSortArgumentException.php b/src/Exception/InvalidSortArgumentException.php new file mode 100644 index 0000000..68e65b6 --- /dev/null +++ b/src/Exception/InvalidSortArgumentException.php @@ -0,0 +1,28 @@ +getPossibleVisitorNames(); + parent::__construct(sprintf( + 'Visitor with name %s does not exist. Possible visitor-names are: %s', + $visitorName, + implode(', ', $possibleVisitorNames) + ), 0, $previous); + } + + /** + * @return array + */ + private function getPossibleVisitorNames(): array + { + $finder = new Finder([dirname(__DIR__) . '/Node/Visitor'], [], [], [], ['VisitorInterface.php'], []); + $finder->sortByName(); + $possibleVisitorNames = []; + foreach ($finder->getIterator() as $file) { + $possibleVisitorNames[] = str_replace('ConditionVisitor.php', '', $file->getRelativePathname()); + } + + return $possibleVisitorNames; + } +} diff --git a/src/Exception/NodeRepresentationClassDoesNotExistException.php b/src/Exception/NodeRepresentationClassDoesNotExistException.php new file mode 100644 index 0000000..2d73dbb --- /dev/null +++ b/src/Exception/NodeRepresentationClassDoesNotExistException.php @@ -0,0 +1,26 @@ +files() + ->in($dirs) + ->exclude($exclude) + ->ignoreDotFiles(true) + ->ignoreVCS(true); + + foreach ($includeFiles as $includeFile) { + $this->name($includeFile); + } + + if (0 === count($includeFiles)) { + foreach ($suffixes as $suffix) { + $this->name('*.' . $suffix); + } + } + + foreach ($excludePaths as $notPath) { + $this->notPath($notPath); + } + + foreach ($excludeFiles as $notName) { + $this->notName($notName); + } + } +} diff --git a/src/Kernel.php b/src/Kernel.php new file mode 100644 index 0000000..d285c66 --- /dev/null +++ b/src/Kernel.php @@ -0,0 +1,53 @@ +load(__DIR__.'/../config/services.yml'); + } + + /** + * @param ContainerBuilder $containerBuilder + * + * @return void + */ + protected function build(ContainerBuilder $containerBuilder): void + { + $containerBuilder->addCompilerPass($this->createCollectingCompilerPass()); + } + + /** + * @return CompilerPassInterface + */ + private function createCollectingCompilerPass(): CompilerPassInterface + { + return new Compiler(); + } +} diff --git a/src/Node/AbstractProcessor.php b/src/Node/AbstractProcessor.php new file mode 100644 index 0000000..b056fef --- /dev/null +++ b/src/Node/AbstractProcessor.php @@ -0,0 +1,68 @@ +nodeOccurrenceList = $nodeOccurrenceList; + } + + /** + * @param Expr $node + * + * @return BooleanNot + */ + protected function negate(Expr $node): BooleanNot + { + return new BooleanNot($node, $node->getAttributes()); + } + + /** + * @param Occurrence $occurrence + * + * @return void + */ + protected function markOccurrenceAsAffected(Occurrence $occurrence): void + { + $occurrence->addAffectedByProcessor($this->getProcessorName(static::class)); + } + + /** + * @param string $classname + * + * @return string + */ + private function getProcessorName(string $classname): string + { + return str_replace('Processor', '', $this->getClassnameWithoutNamespace($classname)); + } + + /** + * @param string $classname + * + * @return string + */ + private function getClassnameWithoutNamespace(string $classname): string + { + $classWithNamespaces = explode('\\', $classname); + + return end($classWithNamespaces); + } +} diff --git a/src/Node/AbstractVisitor.php b/src/Node/AbstractVisitor.php new file mode 100644 index 0000000..838517f --- /dev/null +++ b/src/Node/AbstractVisitor.php @@ -0,0 +1,58 @@ +nodeOccurrenceList = new OccurrenceList(); + } + + /** + * @return OccurrenceList + */ + public function getNodeOccurrenceList(): OccurrenceList + { + return $this->nodeOccurrenceList; + } + + /** + * @param SplFileInfo $file + */ + public function setFile(SplFileInfo $file): void + { + $this->file = $file; + } + + /** + * @param Node $node + * + * @return void + */ + protected function addNodeOccurrence(Node $node): void + { + $occurrence = new Occurrence($node, $this->file); + $this->nodeOccurrenceList->addOccurrence($occurrence); + } +} diff --git a/src/Node/ConditionList/Countable.php b/src/Node/ConditionList/Countable.php new file mode 100644 index 0000000..d30fa1e --- /dev/null +++ b/src/Node/ConditionList/Countable.php @@ -0,0 +1,74 @@ + + */ + private $countedConditions; + + /** + * Countable constructor. + */ + public function __construct() + { + $this->countedConditions = new ArrayCollection(); + } + + /** + * @return ArrayCollection + */ + public function getCountedConditions(): ArrayCollection + { + return $this->countedConditions; + } + + /** + * @param Condition $condition + * + * @return void + */ + public function addCondition(Condition $condition): void + { + $foundKey = $this->checkAlreadyExistent($condition); + + if (null === $foundKey) { + $hash = md5($condition->getCondition()); + $countedCondition = new CountedCondition( + $condition->getCondition(), + $condition->getOccurrence() + ); + $this->countedConditions->set($hash, $countedCondition); + } else { + /** @var CountedCondition $countedCondition */ + $countedCondition = $this->countedConditions->get($foundKey); + $countedCondition->addOccurrence($condition->getOccurrence()); + } + } + + /** + * @param Condition $condition + * + * @return string|null + */ + private function checkAlreadyExistent(Condition $condition): ?string + { + $hash = md5($condition->getCondition()); + + if ($this->countedConditions->containsKey($hash)) { + return $hash; + } + + return null; + } +} diff --git a/src/Node/ConditionList/FlipChecking.php b/src/Node/ConditionList/FlipChecking.php new file mode 100644 index 0000000..2de8aa6 --- /dev/null +++ b/src/Node/ConditionList/FlipChecking.php @@ -0,0 +1,64 @@ +getCondition(), ' '.$operator.' ')) { + $flippedCond = $this->flipCondition($condition->getCondition(), $operator); + $flippedCondHash = md5($flippedCond); + if (in_array($flippedCondHash, $this->conditionHashes, true)) { + $condition->setCondition($flippedCond); + $condition->getOccurrence()->setIsFlipped(true); + break; + } + } + } + + $this->conditionHashes[] = md5($condition->getCondition()); + + parent::addCondition($condition); + } + + /** + * @param string $condition + * @param string $operator + * + * @return string + */ + private function flipCondition(string $condition, string $operator): string + { + return sprintf( + '%s %s %s', + substr($condition, strpos($condition, $operator) + strlen($operator) + 1), + $operator, + substr($condition, 0, strpos($condition, $operator) - 1) + ); + } +} diff --git a/src/Node/Expr/BooleanNot/Equal.php b/src/Node/Expr/BooleanNot/Equal.php new file mode 100644 index 0000000..2c631ce --- /dev/null +++ b/src/Node/Expr/BooleanNot/Equal.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/Greater.php b/src/Node/Expr/BooleanNot/Greater.php new file mode 100644 index 0000000..9009d66 --- /dev/null +++ b/src/Node/Expr/BooleanNot/Greater.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/GreaterOrEqual.php b/src/Node/Expr/BooleanNot/GreaterOrEqual.php new file mode 100644 index 0000000..1fe1b34 --- /dev/null +++ b/src/Node/Expr/BooleanNot/GreaterOrEqual.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/Identical.php b/src/Node/Expr/BooleanNot/Identical.php new file mode 100644 index 0000000..7b14a17 --- /dev/null +++ b/src/Node/Expr/BooleanNot/Identical.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/NotEqual.php b/src/Node/Expr/BooleanNot/NotEqual.php new file mode 100644 index 0000000..b928ac6 --- /dev/null +++ b/src/Node/Expr/BooleanNot/NotEqual.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/NotIdentical.php b/src/Node/Expr/BooleanNot/NotIdentical.php new file mode 100644 index 0000000..69fa620 --- /dev/null +++ b/src/Node/Expr/BooleanNot/NotIdentical.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/Smaller.php b/src/Node/Expr/BooleanNot/Smaller.php new file mode 100644 index 0000000..f49ce73 --- /dev/null +++ b/src/Node/Expr/BooleanNot/Smaller.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/BooleanNot/SmallerOrEqual.php b/src/Node/Expr/BooleanNot/SmallerOrEqual.php new file mode 100644 index 0000000..e26b6d5 --- /dev/null +++ b/src/Node/Expr/BooleanNot/SmallerOrEqual.php @@ -0,0 +1,23 @@ +left, $binaryOp->right, $binaryOp->getAttributes()); + } +} diff --git a/src/Node/Expr/NegationInterface.php b/src/Node/Expr/NegationInterface.php new file mode 100644 index 0000000..d4e5f45 --- /dev/null +++ b/src/Node/Expr/NegationInterface.php @@ -0,0 +1,19 @@ +getNode(); + if ($this->isBooleanNotAndHasBinaryOpExpression($node)) { + /** @var Node\Expr\BooleanNot $node */ + /** @var BinaryOp $expression */ + $expression = $node->expr; + + $occurrence->setNode($this->processBinaryOp($expression, $occurrence)); + } + } + + /** + * @param Node $node + * + * @return bool + */ + private function isBooleanNotAndHasBinaryOpExpression(Node $node): bool + { + return $node instanceof BooleanNot && $node->expr instanceof BinaryOp; + } + + /** + * @param string $classname + * + * @return string + */ + private function transformClassname(string $classname): string + { + $classWithNamespaces = explode('\\', $classname); + $classWithoutNamespace = end($classWithNamespaces); + + $targetNamespaces = explode('\\', NegationInterface::class); + array_pop($targetNamespaces); + $targetNamespaces[] = 'BooleanNot'; + $targetNamespace = implode('\\', $targetNamespaces); + + return sprintf( + '%s\\%s', + $targetNamespace, + $classWithoutNamespace + ); + } + + /** + * @param BinaryOp $node + * @param Occurrence $occurrence + * + * @return BinaryOp + */ + private function processBinaryOp(BinaryOp $node, Occurrence $occurrence): BinaryOp + { + $negationClassname = $this->transformClassname(get_class($node)); + + if (class_exists($negationClassname)) { + $this->markOccurrenceAsAffected($occurrence); + + /** @var NegationInterface $negationClass */ + $negationClass = new $negationClassname(); + + return $negationClass->negate($node); + } + + $node = $this->processLogicalOp($node, $occurrence); + + return $node; + } + + /** + * @param BinaryOp $node + * @param Occurrence $occurrence + * + * @return BinaryOp + */ + private function processLogicalOp(BinaryOp $node, Occurrence $occurrence): BinaryOp + { + foreach (['left', 'right'] as $binaryOpSide) { + if ($this->isBooleanNotAndHasBinaryOpExpression($node->$binaryOpSide)) { + $node->$binaryOpSide = $this->processBinaryOp($node->$binaryOpSide->expr, $occurrence); + } + + if ($node->$binaryOpSide instanceof BinaryOp) { + $node->$binaryOpSide = $this->processBinaryOp($node->$binaryOpSide, $occurrence); + } + } + + return $node; + } +} diff --git a/src/Node/Processor/ProcessorInterface.php b/src/Node/Processor/ProcessorInterface.php new file mode 100644 index 0000000..ac8662d --- /dev/null +++ b/src/Node/Processor/ProcessorInterface.php @@ -0,0 +1,27 @@ +getNode(); + + if ($node instanceof Node\Expr\BinaryOp) { + $node = $this->replaceAssignmentInBinaryOp($node, $occurrence); + } else { + $node = $this->replaceAssignment($node, $occurrence); + } + $occurrence->setNode($node); + } + + /** + * @param Node $node + * @param Occurrence $occurrence + * + * @return Node + */ + private function replaceAssignment(Node $node, Occurrence $occurrence): Node + { + if ($node instanceof Node\Expr\Assign) { + $node = $node->expr; + $this->markOccurrenceAsAffected($occurrence); + } + + return $node; + } + + /** + * @param Node\Expr\BinaryOp $node + * @param Occurrence $occurrence + * + * @return Node\Expr\BinaryOp + */ + private function replaceAssignmentInBinaryOp(Node\Expr\BinaryOp $node, Occurrence $occurrence): Node\Expr\BinaryOp + { + foreach (['left', 'right'] as $binaryOpSide) { + if ($node->$binaryOpSide instanceof Node\Expr\BinaryOp) { + $node->$binaryOpSide = $this->replaceAssignmentInBinaryOp($node->$binaryOpSide, $occurrence); + } + $node->$binaryOpSide = $this->replaceAssignment($node->$binaryOpSide, $occurrence); + } + + return $node; + } +} diff --git a/src/Node/Processor/RemoveCastProcessor.php b/src/Node/Processor/RemoveCastProcessor.php new file mode 100644 index 0000000..80fc7a4 --- /dev/null +++ b/src/Node/Processor/RemoveCastProcessor.php @@ -0,0 +1,66 @@ +getNode(); + + if ($node instanceof Node\Expr\BinaryOp) { + $node = $this->replaceCastInBinaryOp($node, $occurrence); + } else { + $node = $this->replaceCast($node, $occurrence); + } + $occurrence->setNode($node); + } + + /** + * @param Node $node + * @param Occurrence $occurrence + * + * @return Node + */ + private function replaceCast(Node $node, Occurrence $occurrence): Node + { + if ($node instanceof Node\Expr\Cast) { + $node = $node->expr; + $this->markOccurrenceAsAffected($occurrence); + } + + return $node; + } + + /** + * @param Node\Expr\BinaryOp $node + * @param Occurrence $occurrence + * + * @return Node\Expr\BinaryOp + */ + private function replaceCastInBinaryOp(Node\Expr\BinaryOp $node, Occurrence $occurrence): Node\Expr\BinaryOp + { + foreach (['left', 'right'] as $binaryOpSide) { + if ($node->$binaryOpSide instanceof Node\Expr\BinaryOp) { + $node->$binaryOpSide = $this->replaceCastInBinaryOp($node->$binaryOpSide, $occurrence); + } + $node->$binaryOpSide = $this->replaceCast($node->$binaryOpSide, $occurrence); + } + + return $node; + } +} diff --git a/src/Node/Processor/RemoveDuplicateBooleanNotProcessor.php b/src/Node/Processor/RemoveDuplicateBooleanNotProcessor.php new file mode 100644 index 0000000..7603e2a --- /dev/null +++ b/src/Node/Processor/RemoveDuplicateBooleanNotProcessor.php @@ -0,0 +1,29 @@ +getNode(); + if ($node instanceof Node\Expr\BooleanNot && $node->expr instanceof Node\Expr\BooleanNot) { + $occurrence->setNode($node->expr->expr); + $this->markOccurrenceAsAffected($occurrence); + } + } +} diff --git a/src/Node/Processor/RemoveSingleFullyQualifiedNameProcessor.php b/src/Node/Processor/RemoveSingleFullyQualifiedNameProcessor.php new file mode 100644 index 0000000..08f5328 --- /dev/null +++ b/src/Node/Processor/RemoveSingleFullyQualifiedNameProcessor.php @@ -0,0 +1,86 @@ +getNode(); + if ($node instanceof Node\Expr\BinaryOp) { + $node = $this->replaceFullyQualifiedNameInBinaryOp($node, $occurrence); + } else { + $node = $this->replaceFullyQualifiedName($node, $occurrence); + } + $occurrence->setNode($node); + } + + /** + * @param Node $node + * @param Occurrence $occurrence + * + * @return Node + */ + private function replaceFullyQualifiedName(Node $node, Occurrence $occurrence): Node + { + /** @var Node\Expr\FuncCall|Node\Expr\ConstFetch $node */ + if (property_exists($node, 'name') && + $node->name instanceof Node\Name\FullyQualified && + 1 === count($node->name->parts) + ) { + /** @var Node\Name\FullyQualified $name */ + $name = $node->name; + $node->name = $this->generateNameNodeFromFullyQualified($name); + $this->markOccurrenceAsAffected($occurrence); + } + + return $node; + } + + /** + * @param Node\Expr\BinaryOp $node + * @param Occurrence $occurrence + * + * @return Node\Expr\BinaryOp + */ + private function replaceFullyQualifiedNameInBinaryOp( + Node\Expr\BinaryOp $node, + Occurrence $occurrence + ): Node\Expr\BinaryOp { + foreach (['left', 'right'] as $binaryOpSide) { + if ($node->$binaryOpSide instanceof Node\Expr\BinaryOp) { + $node->$binaryOpSide = $this->replaceFullyQualifiedNameInBinaryOp($node->$binaryOpSide, $occurrence); + } + $node->$binaryOpSide = $this->replaceFullyQualifiedName($node->$binaryOpSide, $occurrence); + } + + return $node; + } + + /** + * @param Node\Name\FullyQualified $name + * + * @return Node\Name + */ + private function generateNameNodeFromFullyQualified(Node\Name\FullyQualified $name): Node\Name + { + return new Node\Name( + end($name->parts), + $name->getAttributes() + ); + } +} diff --git a/src/Node/Processor/SplitIssetProcessor.php b/src/Node/Processor/SplitIssetProcessor.php new file mode 100644 index 0000000..6bd5340 --- /dev/null +++ b/src/Node/Processor/SplitIssetProcessor.php @@ -0,0 +1,102 @@ +getNode(); + if ($this->isBooleanNotNestedIsset($node)) { + /** @var Node\Expr\BooleanNot $node */ + $node = $node->expr; + $isBooleanNot = true; + } + if ($this->isIssetAndHasMultipleArguments($node)) { + /** @var Isset_ $node */ + $arguments = $node->vars; + $node->vars = []; + + $this->createIssetOccurrences($occurrence, $arguments, $node, $isBooleanNot); + $this->nodeOccurrenceList->removeOccurrence($occurrence); + } + } + + /** + * @param Node $node + * + * @return bool + */ + private function isIssetAndHasMultipleArguments(Node $node): bool + { + return $node instanceof Isset_ && count($node->vars) > 1; + } + + /** + * @param Node $node + * + * @return bool + */ + private function isBooleanNotNestedIsset(Node $node): bool + { + return $node instanceof BooleanNot && $this->isIssetAndHasMultipleArguments($node->expr); + } + + /** + * @param Isset_ $originalNode + * @param Node $var + * @param bool $isBooleanNot + * + * @return Node|Isset_|BooleanNot + */ + private function createNewIssetNode(Isset_ $originalNode, Node $var, bool $isBooleanNot): Node + { + $node = clone $originalNode; + $node->vars[] = $var; + + if ($isBooleanNot) { + $node = $this->negate($node); + } + + return $node; + } + + /** + * @param Occurrence $originalOccurrence + * @param iterable $arguments + * @param Isset_ $originalNode + * @param bool $isBooleanNot + * + * @return void + */ + private function createIssetOccurrences( + Occurrence $originalOccurrence, + iterable $arguments, + Isset_ $originalNode, + bool $isBooleanNot + ): void { + foreach ($arguments as $argument) { + $node = $this->createNewIssetNode($originalNode, $argument, $isBooleanNot); + $occurrence = clone $originalOccurrence; + $occurrence->setNode($node); + $this->markOccurrenceAsAffected($occurrence); + $this->nodeOccurrenceList->addOccurrence($occurrence); + } + } +} diff --git a/src/Node/Processor/SplitLogicalOperatorProcessor.php b/src/Node/Processor/SplitLogicalOperatorProcessor.php new file mode 100644 index 0000000..979d666 --- /dev/null +++ b/src/Node/Processor/SplitLogicalOperatorProcessor.php @@ -0,0 +1,122 @@ +getNode(); + if ($this->isBooleanNotNestedLogicalOperator($node)) { + /** @var Node\Expr\BooleanNot $node */ + $node = $node->expr; + $isBooleanNot = true; + } + if ($this->isLogicalOperator($node)) { + /** @var BinaryOp $node */ + $this->createLogicalOperatorOccurrences($occurrence, $node, $isBooleanNot); + + $this->nodeOccurrenceList->removeOccurrence($occurrence); + } + } + + /** + * @param Node $node + * + * @return bool + */ + private function isLogicalOperator(Node $node): bool + { + return $this->isLogicalOperatorBoolean($node) || $this->isLogicalOperatorLogical($node); + } + + /** + * @param Node $node + * + * @return bool + */ + private function isBooleanNotNestedLogicalOperator(Node $node): bool + { + return $node instanceof BooleanNot && $this->isLogicalOperator($node->expr); + } + + /** + * @param Node\Expr $originalNode + * @param bool $isBooleanNot + * + * @return Node|BooleanNot|BinaryOp + */ + private function createNewLogicalOperatorNode(Node\Expr $originalNode, bool $isBooleanNot): Node + { + $node = clone $originalNode; + + if ($isBooleanNot) { + $node = $this->negate($node); + } + + return $node; + } + + /** + * @param Node $node + * + * @return bool + */ + private function isLogicalOperatorBoolean(Node $node): bool + { + return $node instanceof Node\Expr\BinaryOp\BooleanAnd || + $node instanceof Node\Expr\BinaryOp\BooleanOr; + } + + /** + * @param Node $node + * + * @return bool + */ + private function isLogicalOperatorLogical(Node $node): bool + { + return $node instanceof Node\Expr\BinaryOp\LogicalAnd || + $node instanceof Node\Expr\BinaryOp\LogicalOr; + } + + /** + * @param Occurrence $originalOccurrence + * @param BinaryOp $originalNode + * @param bool $isBooleanNot + * + * @return void + */ + private function createLogicalOperatorOccurrences( + Occurrence $originalOccurrence, + BinaryOp $originalNode, + bool $isBooleanNot + ): void { + foreach (['left', 'right'] as $operatorSide) { + $sideNode = $originalNode->$operatorSide; + + $node = $this->createNewLogicalOperatorNode($sideNode, $isBooleanNot); + $occurrence = clone $originalOccurrence; + $occurrence->setNode($node); + $this->markOccurrenceAsAffected($occurrence); + $this->nodeOccurrenceList->addOccurrence($occurrence); + + $this->process($occurrence); + } + } +} diff --git a/src/Node/ProcessorRunner.php b/src/Node/ProcessorRunner.php new file mode 100644 index 0000000..0cbc4f1 --- /dev/null +++ b/src/Node/ProcessorRunner.php @@ -0,0 +1,45 @@ +processors[] = $processor; + } + + /** + * @param OccurrenceList $nodeOccurrenceList + * + * @return \Generator + */ + public function process(OccurrenceList $nodeOccurrenceList): \Generator + { + /** @var Processor\ProcessorInterface $processor */ + foreach ($this->processors as $counter => $processor) { + $counter++; + $processor->setNodeOccurrenceList($nodeOccurrenceList); + /** @var Occurrence $occurrence */ + foreach ($nodeOccurrenceList->getOccurrences() as $occurrence) { + $processor->process($occurrence); + } + yield $counter; + } + } +} diff --git a/src/Node/ProcessorRunnerInterface.php b/src/Node/ProcessorRunnerInterface.php new file mode 100644 index 0000000..44c0766 --- /dev/null +++ b/src/Node/ProcessorRunnerInterface.php @@ -0,0 +1,19 @@ +nodeRepresentationService = $nodeRepresentationService; + $this->node = $node; + } + + /** + * @param Node $node + * + * @return string|null + */ + protected function representate(Node $node): ?string + { + return $this->nodeRepresentationService->representationForNode($node); + } + + /** + * @param array $arguments + * @param string $implodeBy + * + * @return string + */ + protected function arguments(array $arguments, string $implodeBy = ', '): string + { + return implode($implodeBy, $this->nodeRepresentationService->representationForArguments($arguments)); + } +} diff --git a/src/Node/Representation/Arg.php b/src/Node/Representation/Arg.php new file mode 100644 index 0000000..83858db --- /dev/null +++ b/src/Node/Representation/Arg.php @@ -0,0 +1,30 @@ +node; + + $byRef = $node->byRef ? '&' : ''; + + $unpack = $node->unpack ? '...' : ''; + + return sprintf( + '%s%s%s', + $unpack, + $byRef, + $this->representate($node->value) + ); + } +} diff --git a/src/Node/Representation/Expr/ArrayDimFetch.php b/src/Node/Representation/Expr/ArrayDimFetch.php new file mode 100644 index 0000000..8de7ac7 --- /dev/null +++ b/src/Node/Representation/Expr/ArrayDimFetch.php @@ -0,0 +1,28 @@ +node; + + + return sprintf( + '%s[%s]', + $this->representate($node->var), + null === $node->dim ? '' : $this->representate($node->dim) + ); + } +} diff --git a/src/Node/Representation/Expr/ArrayItem.php b/src/Node/Representation/Expr/ArrayItem.php new file mode 100644 index 0000000..c87330f --- /dev/null +++ b/src/Node/Representation/Expr/ArrayItem.php @@ -0,0 +1,41 @@ +node; + + $byRef = $node->byRef ? '&' : ''; + + if (null !== $node->key) { + return sprintf( + '%s => %s%s', + $this->representate($node->key), + $byRef, + $this->representate($node->value) + ); + } + + $unpack = $node->unpack ? '...' : ''; + + return sprintf( + '%s%s%s', + $unpack, + $byRef, + $this->representate($node->value) + ); + } +} diff --git a/src/Node/Representation/Expr/Array_.php b/src/Node/Representation/Expr/Array_.php new file mode 100644 index 0000000..587a45b --- /dev/null +++ b/src/Node/Representation/Expr/Array_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '[%s]', + $this->arguments($node->items) + ); + } +} diff --git a/src/Node/Representation/Expr/ArrowFunction.php b/src/Node/Representation/Expr/ArrowFunction.php new file mode 100644 index 0000000..00dddd8 --- /dev/null +++ b/src/Node/Representation/Expr/ArrowFunction.php @@ -0,0 +1,40 @@ +node; + + $static = ''; + if ($node->static) { + $static = 'static '; + } + + $returnType = ''; + if (null !== $node->returnType) { + $returnType = sprintf(': %s', $this->representate($node->returnType)); + } + + return sprintf( + '(%sfn%s(%s)%s => %s)', + $static, + $node->byRef ? '&' : '', + $this->arguments($node->params), + $returnType, + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Assign.php b/src/Node/Representation/Expr/Assign.php new file mode 100644 index 0000000..98d0a82 --- /dev/null +++ b/src/Node/Representation/Expr/Assign.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s = %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/BitwiseAnd.php b/src/Node/Representation/Expr/AssignOp/BitwiseAnd.php new file mode 100644 index 0000000..bf78140 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/BitwiseAnd.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s &= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/BitwiseOr.php b/src/Node/Representation/Expr/AssignOp/BitwiseOr.php new file mode 100644 index 0000000..d85f373 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/BitwiseOr.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s |= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/BitwiseXor.php b/src/Node/Representation/Expr/AssignOp/BitwiseXor.php new file mode 100644 index 0000000..5c4d7a2 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/BitwiseXor.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s ^= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Coalesce.php b/src/Node/Representation/Expr/AssignOp/Coalesce.php new file mode 100644 index 0000000..b4ef3ae --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Coalesce.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s ??= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Concat.php b/src/Node/Representation/Expr/AssignOp/Concat.php new file mode 100644 index 0000000..870bf5d --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Concat.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s .= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Div.php b/src/Node/Representation/Expr/AssignOp/Div.php new file mode 100644 index 0000000..f38ba89 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Div.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s /= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Minus.php b/src/Node/Representation/Expr/AssignOp/Minus.php new file mode 100644 index 0000000..5ec972f --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Minus.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s -= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Mod.php b/src/Node/Representation/Expr/AssignOp/Mod.php new file mode 100644 index 0000000..fab0bf9 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Mod.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s %%= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Mul.php b/src/Node/Representation/Expr/AssignOp/Mul.php new file mode 100644 index 0000000..d315c7c --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Mul.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s *= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Plus.php b/src/Node/Representation/Expr/AssignOp/Plus.php new file mode 100644 index 0000000..8318fe1 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Plus.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s += %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/Pow.php b/src/Node/Representation/Expr/AssignOp/Pow.php new file mode 100644 index 0000000..0d37d1b --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/Pow.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s **= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/ShiftLeft.php b/src/Node/Representation/Expr/AssignOp/ShiftLeft.php new file mode 100644 index 0000000..d601630 --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/ShiftLeft.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s <<= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignOp/ShiftRight.php b/src/Node/Representation/Expr/AssignOp/ShiftRight.php new file mode 100644 index 0000000..613fa9b --- /dev/null +++ b/src/Node/Representation/Expr/AssignOp/ShiftRight.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s >>= %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/AssignRef.php b/src/Node/Representation/Expr/AssignRef.php new file mode 100644 index 0000000..6b7ce40 --- /dev/null +++ b/src/Node/Representation/Expr/AssignRef.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s =& %s', + $this->representate($node->var), + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/BinaryOp.php b/src/Node/Representation/Expr/BinaryOp.php new file mode 100644 index 0000000..78909ab --- /dev/null +++ b/src/Node/Representation/Expr/BinaryOp.php @@ -0,0 +1,50 @@ +node; + + $format = '%s %s %s'; + + if (BooleanOr::class === get_class($node) && + (is_callable([$node->left, 'getOperatorSigil']) || + is_callable([$node->right, 'getOperatorSigil'])) + ) { + $format = sprintf('(%s)', $format); + } + + return sprintf( + $format, + $this->representate($node->left), + $node->getOperatorSigil(), + $this->representate($node->right) + ); + } +} diff --git a/src/Node/Representation/Expr/BinaryOp/Concat.php b/src/Node/Representation/Expr/BinaryOp/Concat.php new file mode 100644 index 0000000..cc49a7a --- /dev/null +++ b/src/Node/Representation/Expr/BinaryOp/Concat.php @@ -0,0 +1,28 @@ +node; + + return sprintf( + '%s%s%s', + $this->representate($node->left), + $node->getOperatorSigil(), + $this->representate($node->right) + ); + } +} diff --git a/src/Node/Representation/Expr/BitwiseNot.php b/src/Node/Representation/Expr/BitwiseNot.php new file mode 100644 index 0000000..2e547e0 --- /dev/null +++ b/src/Node/Representation/Expr/BitwiseNot.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s%s', + '~', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/BooleanNot.php b/src/Node/Representation/Expr/BooleanNot.php new file mode 100644 index 0000000..19f26bd --- /dev/null +++ b/src/Node/Representation/Expr/BooleanNot.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s%s', + '!', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Cast.php b/src/Node/Representation/Expr/Cast.php new file mode 100644 index 0000000..dfd920a --- /dev/null +++ b/src/Node/Representation/Expr/Cast.php @@ -0,0 +1,32 @@ +node; + + $type = strtolower(substr(strrchr(get_class($node), '\\'), 1)); + if ('_' === substr($type, -1)) { + $type = substr($type, 0, -1); + } + + return sprintf( + '(%s) %s', + $type, + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Cast/Double.php b/src/Node/Representation/Expr/Cast/Double.php new file mode 100644 index 0000000..a8d7a5a --- /dev/null +++ b/src/Node/Representation/Expr/Cast/Double.php @@ -0,0 +1,34 @@ + 'double', + \PhpParser\Node\Expr\Cast\Double::KIND_FLOAT => 'float', + \PhpParser\Node\Expr\Cast\Double::KIND_REAL => 'real', + ]; + + /** + * @return string + */ + public function representation(): string + { + /** @var \PhpParser\Node\Expr\Cast\Double $node */ + $node = $this->node; + + return sprintf( + '(%s) %s', + self::$doubleTypes[$node->getAttribute('kind')], + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/ClassConstFetch.php b/src/Node/Representation/Expr/ClassConstFetch.php new file mode 100644 index 0000000..4c2351b --- /dev/null +++ b/src/Node/Representation/Expr/ClassConstFetch.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s::%s', + $this->representate($node->class), + $this->representate($node->name) + ); + } +} diff --git a/src/Node/Representation/Expr/Clone_.php b/src/Node/Representation/Expr/Clone_.php new file mode 100644 index 0000000..dc5165c --- /dev/null +++ b/src/Node/Representation/Expr/Clone_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'clone %s', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Closure.php b/src/Node/Representation/Expr/Closure.php new file mode 100644 index 0000000..e30812b --- /dev/null +++ b/src/Node/Representation/Expr/Closure.php @@ -0,0 +1,42 @@ +node; + + $use = ''; + if (count($node->uses)) { + $use = sprintf( + 'use (%s) ', + $this->arguments($node->uses) + ); + } + + $static = ''; + if ($node->static) { + $static = 'static '; + } + + return sprintf( + '%sfunction%s(%s) %s{ /* CLOSURE */ }', + $static, + $node->byRef ? '&' : '', + $this->arguments($node->params), + $use + ); + } +} diff --git a/src/Node/Representation/Expr/ClosureUse.php b/src/Node/Representation/Expr/ClosureUse.php new file mode 100644 index 0000000..8010b8e --- /dev/null +++ b/src/Node/Representation/Expr/ClosureUse.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s%s', + $node->byRef ? '&' : '', + $this->representate($node->var) + ); + } +} diff --git a/src/Node/Representation/Expr/ConstFetch.php b/src/Node/Representation/Expr/ConstFetch.php new file mode 100644 index 0000000..a788f67 --- /dev/null +++ b/src/Node/Representation/Expr/ConstFetch.php @@ -0,0 +1,23 @@ +node; + + return $this->representate($node->name); + } +} diff --git a/src/Node/Representation/Expr/Empty_.php b/src/Node/Representation/Expr/Empty_.php new file mode 100644 index 0000000..227b542 --- /dev/null +++ b/src/Node/Representation/Expr/Empty_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'empty(%s)', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/ErrorSuppress.php b/src/Node/Representation/Expr/ErrorSuppress.php new file mode 100644 index 0000000..0d829aa --- /dev/null +++ b/src/Node/Representation/Expr/ErrorSuppress.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '@%s', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Eval_.php b/src/Node/Representation/Expr/Eval_.php new file mode 100644 index 0000000..d1c38bf --- /dev/null +++ b/src/Node/Representation/Expr/Eval_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'eval(%s)', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Exit_.php b/src/Node/Representation/Expr/Exit_.php new file mode 100644 index 0000000..bcbb3a9 --- /dev/null +++ b/src/Node/Representation/Expr/Exit_.php @@ -0,0 +1,30 @@ +node; + + if (null === $node->expr) { + return 'exit'; + } + + return sprintf( + 'exit(%s)', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/FuncCall.php b/src/Node/Representation/Expr/FuncCall.php new file mode 100644 index 0000000..c3f82ad --- /dev/null +++ b/src/Node/Representation/Expr/FuncCall.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s(%s)', + $this->representate($node->name), + $this->arguments($node->args) + ); + } +} diff --git a/src/Node/Representation/Expr/Include_.php b/src/Node/Representation/Expr/Include_.php new file mode 100644 index 0000000..4bbb64e --- /dev/null +++ b/src/Node/Representation/Expr/Include_.php @@ -0,0 +1,35 @@ + 'include', + \PhpParser\Node\Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', + \PhpParser\Node\Expr\Include_::TYPE_REQUIRE => 'require', + \PhpParser\Node\Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once', + ]; + + /** + * @return string + */ + public function representation(): string + { + /** @var \PhpParser\Node\Expr\Include_ $node */ + $node = $this->node; + + return sprintf( + '%s(%s)', + self::$includeFunctions[$node->type], + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Instanceof_.php b/src/Node/Representation/Expr/Instanceof_.php new file mode 100644 index 0000000..d512ca3 --- /dev/null +++ b/src/Node/Representation/Expr/Instanceof_.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s instanceof %s', + $this->representate($node->expr), + $this->representate($node->class) + ); + } +} diff --git a/src/Node/Representation/Expr/Isset_.php b/src/Node/Representation/Expr/Isset_.php new file mode 100644 index 0000000..adcbd5c --- /dev/null +++ b/src/Node/Representation/Expr/Isset_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'isset(%s)', + $this->arguments($node->vars) + ); + } +} diff --git a/src/Node/Representation/Expr/List_.php b/src/Node/Representation/Expr/List_.php new file mode 100644 index 0000000..26be8de --- /dev/null +++ b/src/Node/Representation/Expr/List_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'list(%s)', + $this->arguments($node->items) + ); + } +} diff --git a/src/Node/Representation/Expr/MethodCall.php b/src/Node/Representation/Expr/MethodCall.php new file mode 100644 index 0000000..52e153b --- /dev/null +++ b/src/Node/Representation/Expr/MethodCall.php @@ -0,0 +1,28 @@ +node; + + return sprintf( + '%s->%s(%s)', + $this->representate($node->var), + $this->representate($node->name), + $this->arguments($node->args) + ); + } +} diff --git a/src/Node/Representation/Expr/New_.php b/src/Node/Representation/Expr/New_.php new file mode 100644 index 0000000..1056f23 --- /dev/null +++ b/src/Node/Representation/Expr/New_.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '(new %s(%s))', + $this->representate($node->class), + $this->arguments($node->args) + ); + } +} diff --git a/src/Node/Representation/Expr/PostDec.php b/src/Node/Representation/Expr/PostDec.php new file mode 100644 index 0000000..0e6ec0d --- /dev/null +++ b/src/Node/Representation/Expr/PostDec.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '%s--', + $this->representate($node->var) + ); + } +} diff --git a/src/Node/Representation/Expr/PostInc.php b/src/Node/Representation/Expr/PostInc.php new file mode 100644 index 0000000..59fbc58 --- /dev/null +++ b/src/Node/Representation/Expr/PostInc.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '%s++', + $this->representate($node->var) + ); + } +} diff --git a/src/Node/Representation/Expr/PreDec.php b/src/Node/Representation/Expr/PreDec.php new file mode 100644 index 0000000..58b1880 --- /dev/null +++ b/src/Node/Representation/Expr/PreDec.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '--%s', + $this->representate($node->var) + ); + } +} diff --git a/src/Node/Representation/Expr/PreInc.php b/src/Node/Representation/Expr/PreInc.php new file mode 100644 index 0000000..42faefb --- /dev/null +++ b/src/Node/Representation/Expr/PreInc.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '++%s', + $this->representate($node->var) + ); + } +} diff --git a/src/Node/Representation/Expr/Print_.php b/src/Node/Representation/Expr/Print_.php new file mode 100644 index 0000000..2ffaba4 --- /dev/null +++ b/src/Node/Representation/Expr/Print_.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'print(%s)', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/PropertyFetch.php b/src/Node/Representation/Expr/PropertyFetch.php new file mode 100644 index 0000000..8f112a4 --- /dev/null +++ b/src/Node/Representation/Expr/PropertyFetch.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s->%s', + $this->representate($node->var), + $this->representate($node->name) + ); + } +} diff --git a/src/Node/Representation/Expr/ShellExec.php b/src/Node/Representation/Expr/ShellExec.php new file mode 100644 index 0000000..ab4f172 --- /dev/null +++ b/src/Node/Representation/Expr/ShellExec.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + 'shell_exec(%s)', + $this->arguments($node->parts) + ); + } +} diff --git a/src/Node/Representation/Expr/StaticCall.php b/src/Node/Representation/Expr/StaticCall.php new file mode 100644 index 0000000..f9d923f --- /dev/null +++ b/src/Node/Representation/Expr/StaticCall.php @@ -0,0 +1,28 @@ +node; + + return sprintf( + '%s::%s(%s)', + $this->representate($node->class), + $this->representate($node->name), + $this->arguments($node->args) + ); + } +} diff --git a/src/Node/Representation/Expr/StaticPropertyFetch.php b/src/Node/Representation/Expr/StaticPropertyFetch.php new file mode 100644 index 0000000..413e5f4 --- /dev/null +++ b/src/Node/Representation/Expr/StaticPropertyFetch.php @@ -0,0 +1,27 @@ +node; + + return sprintf( + '%s::%s', + $this->representate($node->class), + $this->representate($node->name) + ); + } +} diff --git a/src/Node/Representation/Expr/Ternary.php b/src/Node/Representation/Expr/Ternary.php new file mode 100644 index 0000000..ee995e7 --- /dev/null +++ b/src/Node/Representation/Expr/Ternary.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '%s', + $this->representate($node->cond) + ); + } +} diff --git a/src/Node/Representation/Expr/UnaryMinus.php b/src/Node/Representation/Expr/UnaryMinus.php new file mode 100644 index 0000000..5a1f454 --- /dev/null +++ b/src/Node/Representation/Expr/UnaryMinus.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '-%s', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/UnaryPlus.php b/src/Node/Representation/Expr/UnaryPlus.php new file mode 100644 index 0000000..9e00ca4 --- /dev/null +++ b/src/Node/Representation/Expr/UnaryPlus.php @@ -0,0 +1,26 @@ +node; + + return sprintf( + '+%s', + $this->representate($node->expr) + ); + } +} diff --git a/src/Node/Representation/Expr/Variable.php b/src/Node/Representation/Expr/Variable.php new file mode 100644 index 0000000..9f7bfd1 --- /dev/null +++ b/src/Node/Representation/Expr/Variable.php @@ -0,0 +1,33 @@ +node; + + $name = $node->name; + + if (!is_string($name)) { + $name = $this->representate($name); + } + + return sprintf( + '%s%s', + '$', + $name + ); + } +} diff --git a/src/Node/Representation/Identifier.php b/src/Node/Representation/Identifier.php new file mode 100644 index 0000000..8daa162 --- /dev/null +++ b/src/Node/Representation/Identifier.php @@ -0,0 +1,21 @@ +node; + + return $node->name; + } +} diff --git a/src/Node/Representation/Name.php b/src/Node/Representation/Name.php new file mode 100644 index 0000000..ed15429 --- /dev/null +++ b/src/Node/Representation/Name.php @@ -0,0 +1,21 @@ +node; + + return implode('\\', $node->parts); + } +} diff --git a/src/Node/Representation/Name/FullyQualified.php b/src/Node/Representation/Name/FullyQualified.php new file mode 100644 index 0000000..72706f7 --- /dev/null +++ b/src/Node/Representation/Name/FullyQualified.php @@ -0,0 +1,23 @@ +node; + + return sprintf('\\%s', implode('\\', $node->parts)); + } +} diff --git a/src/Node/Representation/Param.php b/src/Node/Representation/Param.php new file mode 100644 index 0000000..b862c20 --- /dev/null +++ b/src/Node/Representation/Param.php @@ -0,0 +1,33 @@ +node; + + $byRef = $node->byRef ? '&' : ''; + $type = $node->type ? $node->type.' ' : ''; + $default = $node->default ? ' = '. $this->representate($node->default) : ''; + $variadic = $node->variadic ? '...' : ''; + + return sprintf( + '%s%s%s%s%s', + $type, + $variadic, + $byRef, + $this->representate($node->var), + $default + ); + } +} diff --git a/src/Node/Representation/RepresentationInterface.php b/src/Node/Representation/RepresentationInterface.php new file mode 100644 index 0000000..e0353cd --- /dev/null +++ b/src/Node/Representation/RepresentationInterface.php @@ -0,0 +1,15 @@ +node; + + return (string) $node->value; + } +} diff --git a/src/Node/Representation/Scalar/Encapsed.php b/src/Node/Representation/Scalar/Encapsed.php new file mode 100644 index 0000000..52ce902 --- /dev/null +++ b/src/Node/Representation/Scalar/Encapsed.php @@ -0,0 +1,23 @@ +node; + + return sprintf('"%s"', $this->arguments($node->parts, '')); + } +} diff --git a/src/Node/Representation/Scalar/EncapsedStringPart.php b/src/Node/Representation/Scalar/EncapsedStringPart.php new file mode 100644 index 0000000..1cf3bb3 --- /dev/null +++ b/src/Node/Representation/Scalar/EncapsedStringPart.php @@ -0,0 +1,23 @@ +node; + + return $node->value; + } +} diff --git a/src/Node/Representation/Scalar/LNumber.php b/src/Node/Representation/Scalar/LNumber.php new file mode 100644 index 0000000..e60dcf8 --- /dev/null +++ b/src/Node/Representation/Scalar/LNumber.php @@ -0,0 +1,23 @@ +node; + + return (string) $node->value; + } +} diff --git a/src/Node/Representation/Scalar/MagicConst.php b/src/Node/Representation/Scalar/MagicConst.php new file mode 100644 index 0000000..5499edc --- /dev/null +++ b/src/Node/Representation/Scalar/MagicConst.php @@ -0,0 +1,23 @@ +node; + + return sprintf('%s', $node->getName()); + } +} diff --git a/src/Node/Representation/Scalar/String_.php b/src/Node/Representation/Scalar/String_.php new file mode 100644 index 0000000..768fe48 --- /dev/null +++ b/src/Node/Representation/Scalar/String_.php @@ -0,0 +1,23 @@ +node; + + return sprintf("'%s'", $node->value); + } +} diff --git a/src/Node/Representation/VarLikeIdentifier.php b/src/Node/Representation/VarLikeIdentifier.php new file mode 100644 index 0000000..9d95580 --- /dev/null +++ b/src/Node/Representation/VarLikeIdentifier.php @@ -0,0 +1,25 @@ +node; + + return sprintf( + '%s%s', + '$', + $node->name + ); + } +} diff --git a/src/Node/Traverser.php b/src/Node/Traverser.php new file mode 100644 index 0000000..f02d467 --- /dev/null +++ b/src/Node/Traverser.php @@ -0,0 +1,52 @@ +updateFileInVisitors($file); + } + + /** + * @return int + */ + public function getNodeOccurrencesCount(): int + { + $count = 0; + + foreach ($this->visitors as $visitor) { + if ($visitor instanceof VisitorInterface) { + $count += $visitor->getNodeOccurrenceList()->count(); + } + } + + return $count; + } + + /** + * @param SplFileInfo $file + * + * @return void + */ + private function updateFileInVisitors(SplFileInfo $file): void + { + foreach ($this->visitors as $visitor) { + if ($visitor instanceof VisitorInterface) { + $visitor->setFile($file); + } + } + } +} diff --git a/src/Node/Visitor/BooleanReturnConditionVisitor.php b/src/Node/Visitor/BooleanReturnConditionVisitor.php new file mode 100644 index 0000000..ed3a7ce --- /dev/null +++ b/src/Node/Visitor/BooleanReturnConditionVisitor.php @@ -0,0 +1,36 @@ +returnType; + if ($returnType instanceof Node\Identifier && + 'bool' === strtolower($returnType->name) && + count($node->stmts) + ) { + /** @var Node\Stmt\Return_ $return */ + $return = end($node->stmts); + $this->addNodeOccurrence($return->expr); + } + } + + return null; + } +} diff --git a/src/Node/Visitor/CoalesceConditionVisitor.php b/src/Node/Visitor/CoalesceConditionVisitor.php new file mode 100644 index 0000000..a773b8b --- /dev/null +++ b/src/Node/Visitor/CoalesceConditionVisitor.php @@ -0,0 +1,27 @@ +addNodeOccurrence($node->left); + } + + return null; + } +} diff --git a/src/Node/Visitor/ElseIfConditionVisitor.php b/src/Node/Visitor/ElseIfConditionVisitor.php new file mode 100644 index 0000000..702beb4 --- /dev/null +++ b/src/Node/Visitor/ElseIfConditionVisitor.php @@ -0,0 +1,29 @@ +elseifs as $elseif) { + $this->addNodeOccurrence($elseif->cond); + } + } + + return null; + } +} diff --git a/src/Node/Visitor/IfConditionVisitor.php b/src/Node/Visitor/IfConditionVisitor.php new file mode 100644 index 0000000..65959a7 --- /dev/null +++ b/src/Node/Visitor/IfConditionVisitor.php @@ -0,0 +1,27 @@ +addNodeOccurrence($node->cond); + } + + return null; + } +} diff --git a/src/Node/Visitor/TernaryConditionVisitor.php b/src/Node/Visitor/TernaryConditionVisitor.php new file mode 100644 index 0000000..d36bcf5 --- /dev/null +++ b/src/Node/Visitor/TernaryConditionVisitor.php @@ -0,0 +1,27 @@ +addNodeOccurrence($node->cond); + } + + return null; + } +} diff --git a/src/Node/Visitor/VisitorInterface.php b/src/Node/Visitor/VisitorInterface.php new file mode 100644 index 0000000..6be75b0 --- /dev/null +++ b/src/Node/Visitor/VisitorInterface.php @@ -0,0 +1,25 @@ +transformClassname(get_class($node)); + if (!class_exists($representationClassname)) { + $representationClassname = $this->getAbstractClassname($representationClassname); + } + + if (!class_exists($representationClassname)) { + throw new NodeRepresentationClassDoesNotExistException($node); + } + + /** @var RepresentationInterface $representationClass */ + $representationClass = new $representationClassname($this, $node); + + return $representationClass->representation(); + } + + /** + * @param array $arguments + * + * @return array + */ + public function representationForArguments(array $arguments): array + { + if (0 === count($arguments)) { + return []; + } + + $representedArguments = []; + + foreach ($arguments as $key => $argument) { + $representedArguments[$key] = $this->representationForNode($argument); + } + + return $representedArguments; + } + + /** + * @param string $classname + * + * @return string + */ + private function transformClassname(string $classname): string + { + $classWithNamespaces = explode('\\', $classname); + + $keyPhpParser = array_search('PhpParser', $classWithNamespaces, true); + unset($classWithNamespaces[$keyPhpParser]); + + $keyNode = array_search('Node', $classWithNamespaces, true); + unset($classWithNamespaces[$keyNode]); + + return sprintf( + '%s\\%s', + 'Isfett\\PhpAnalyzer\\Node\\Representation', + implode('\\', $classWithNamespaces) + ); + } + + /** + * @param string $originalClassname + * + * @return string + */ + private function getAbstractClassname(string $originalClassname): string + { + $classnameWithNamespaces = explode('\\', $originalClassname); + + unset($classnameWithNamespaces[array_key_last($classnameWithNamespaces)]); + + return implode('\\', $classnameWithNamespaces); + } +} diff --git a/src/Service/SortService.php b/src/Service/SortService.php new file mode 100644 index 0000000..6a0625c --- /dev/null +++ b/src/Service/SortService.php @@ -0,0 +1,50 @@ +getSortFields($sortConfiguration), + $sortConfiguration->getFirstResult(), + $sortConfiguration->getMaxResults() + ); + + return $collection->matching($criteria); + } + + /** + * @param Sort $sortConfiguration + * + * @return array + */ + private function getSortFields(Sort $sortConfiguration): array + { + $sortFields = []; + + /** @var SortField $sortField */ + foreach ($sortConfiguration->getFields() as $sortField) { + $sortFields[$sortField->getField()] = $sortField->getDirection(); + } + + return $sortFields; + } +} diff --git a/tests/Integration/Console/ApplicationTest.php b/tests/Integration/Console/ApplicationTest.php index 059c3c6..8c573d6 100644 --- a/tests/Integration/Console/ApplicationTest.php +++ b/tests/Integration/Console/ApplicationTest.php @@ -13,7 +13,9 @@ */ class ApplicationTest extends TestCase { - const APPLICATION_INFO = 'php-analyzer 1.0.0 by Christopher Stenke '; + /** @var string */ + private const APPLICATION_INFO = 'php-analyzer 1.0.0 by Christopher Stenke ' . PHP_EOL; + /** @var Application */ private $application; @@ -84,7 +86,7 @@ public function testApplicationAndAuthorInfoIsHiddenWhenInQuietMode(): void * @throws \Exception * @throws \Throwable */ - public function testApplicationVerswion(): void + public function testApplicationVersion(): void { $input = new ArrayInput([ '--version' => true, @@ -92,21 +94,7 @@ public function testApplicationVerswion(): void $output = new BufferedOutput(); $exitCode = $this->application->doRun($input, $output); - - $this->assertStringStartsWith( - self::APPLICATION_INFO, - $output->fetch() - ); - $this->assertEquals(Application::EXIT_CODE_SUCCESS, $exitCode); - - $input = new ArrayInput([ - '-v' => true, - ]); - - $output = new BufferedOutput(); - $this->application->doRun($input, $output); - - $this->assertStringStartsWith( + $this->assertEquals( self::APPLICATION_INFO, $output->fetch() ); diff --git a/tests/Integration/Console/Command/MostUsedConditionsCommandTest.php b/tests/Integration/Console/Command/MostUsedConditionsCommandTest.php new file mode 100644 index 0000000..59f9447 --- /dev/null +++ b/tests/Integration/Console/Command/MostUsedConditionsCommandTest.php @@ -0,0 +1,139 @@ +mostUsedConditionsCommand = new MostUsedConditionsCommand( + new FinderBuilder(), + new ConditionListBuilder(), + new VisitorBuilder(), + new ProcessorBuilder(), + new SortConfigurationBuilder(), + new ProcessorRunner(), + new NodeRepresentationService(), + new SortService() + ); + + $this->application = new Application(); + $this->application->addCommands([$this->mostUsedConditionsCommand]); + } + + /** + * @return void + * @throws \Exception + * @throws \Throwable + */ + public function testRun(): void + { + $input = new ArrayInput([ + 'most-used-conditions', + 'directory' => __DIR__ . '/../../../../tests/data', + '--include-files' => 'most_used_conditions_integrationtest.php', + '--visitors' => 'If,ElseIf,Ternary,Coalesce,BooleanReturn', + '--processors' => 'SplitIsset,SplitLogicalOperator,NegateBooleanNot,RemoveAssignment,RemoveCast,RemoveDuplicateBooleanNot,RemoveSingleFullyQualifiedName', + '--with-flip-check' => true, + + ], $this->mostUsedConditionsCommand->getDefinition()); + + $output = new BufferedOutput(); + $exitCode = $this->mostUsedConditionsCommand->run($input, $output); + $outputText = $output->fetch(); + + $this->assertEquals(Application::EXIT_CODE_SUCCESS, $exitCode); + $this->assertStringStartsWith( + 'Starting most-used-conditions command', + $outputText + ); + + $expectedOutput = <<\$_GET['page'] | 1 | +| most_used_conditions_integrationtest.php:23 | | +|------------------------------------------------------------------------------------------------------|-------| +| \$i > 3 | 1 | +| most_used_conditions_integrationtest.php:13 (NegateBooleanNot) | | +|------------------------------------------------------------------------------------------------------|-------| +| \$i >= 13 | 1 | +| most_used_conditions_integrationtest.php:15 (NegateBooleanNot) | | +|------------------------------------------------------------------------------------------------------|-------| +| \$someVar | 1 | +| most_used_conditions_integrationtest.php:11 (RemoveCast) | | +|------------------------------------------------------------------------------------------------------|-------| +| \$someVar !== 0 | 1 | +| most_used_conditions_integrationtest.php:11 (RemoveCast) | | +|------------------------------------------------------------------------------------------------------|-------| +| \$this->getUser() | 1 | +| most_used_conditions_integrationtest.php:9 (RemoveAssignment) | | +|------------------------------------------------------------------------------------------------------|-------| +| \$x === \$y | 1 | +| most_used_conditions_integrationtest.php:27-28 | | +|------------------------------------------------------------------------------------------------------|-------| +| 30 !== date('d') | 1 | +| most_used_conditions_integrationtest.php:17 (SplitLogicalOperator, Negate | | +| BooleanNot) | | +|------------------------------------------------------------------------------------------------------|-------| +| isset(\$_GET['page']) | 1 | +| most_used_conditions_integrationtest.php:21 | | +|------------------------------------------------------------------------------------------------------|-------| +| isset(\$_SESSION['user']) | 1 | +| most_used_conditions_integrationtest.php:3 (SplitIsset) | | +|------------------------------------------------------------------------------------------------------|-------| +| isset(\$_SESSION['user']['id']) | 1 | +| most_used_conditions_integrationtest.php:3 (SplitIsset) | | +|------------------------------------------------------------------------------------------------------|-------| +| strtolower('Chris') === 'chris' | 1 | +| most_used_conditions_integrationtest.php:19 (RemoveSingleFullyQualifiedNa | | +| me) | | +|------------------------------------------------------------------------------------------------------|-------| +| 3771 === 1337 | 2 | +| most_used_conditions_integrationtest.php:7 | | +| most_used_conditions_integrationtest.php:5 (flipped) (SplitLogicalOperato | | +| r, NegateBooleanNot) | | +|------------------------------------------------------------------------------------------------------|-------| +| null === \$user | 2 | +| most_used_conditions_integrationtest.php:5 (SplitLogicalOperator, NegateB | | +| ooleanNot) | | +| most_used_conditions_integrationtest.php:17 (SplitLogicalOperator, Remove | | +| DuplicateBooleanNot) | | ++------------------------------------------------------------------------------------------------------+-------+ +EOT; + $this->assertStringContainsString($expectedOutput, $outputText); + } +} diff --git a/tests/Unit/Builder/ConditionListBuilderTest.php b/tests/Unit/Builder/ConditionListBuilderTest.php new file mode 100644 index 0000000..7752d52 --- /dev/null +++ b/tests/Unit/Builder/ConditionListBuilderTest.php @@ -0,0 +1,52 @@ +builder = new ConditionListBuilder(); + } + + /** + * @return void + */ + public function testGetConditionList(): void + { + $conditionList = $this->builder + ->setIsFlipCheckingAware(false) + ->getConditionList(); + + $this->assertInstanceOf(ConditionList::class, $conditionList); + } + + /** + * @return void + */ + public function testGetFlipCheckingConditionList(): void + { + $conditionList = $this->builder + ->setIsFlipCheckingAware(true) + ->getConditionList(); + + $this->assertInstanceOf(FlipChecking::class, $conditionList); + } +} diff --git a/tests/Unit/Builder/FinderBuilderTest.php b/tests/Unit/Builder/FinderBuilderTest.php new file mode 100644 index 0000000..7ce7d1a --- /dev/null +++ b/tests/Unit/Builder/FinderBuilderTest.php @@ -0,0 +1,109 @@ +builder = new FinderBuilder(); + } + + /** + * @return void + */ + public function testBuilderWithIncludeFiles(): void + { + $finder = $this->builder + ->setDirectories([dirname(__DIR__)]) + ->setIncludeFiles(['Test.php']) + ->setExcludes(['*cache*.php']) + ->setSuffixes(['php']) + ->setExcludeFiles([ 'src/AppKernel.php']) + ->setExcludePaths(['vendor']) + ->getFinder(); + + $this->assertCount(1, $this->getPrivateValueFromReflection('names', $finder)); + $this->assertContains('Test.php', $this->getPrivateValueFromReflection('names', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('notNames', $finder)); + $this->assertContains('src/AppKernel.php', $this->getPrivateValueFromReflection('notNames', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('exclude', $finder)); + $this->assertContains('*cache*.php', $this->getPrivateValueFromReflection('exclude', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('dirs', $finder)); + $this->assertContains(dirname(__DIR__), $this->getPrivateValueFromReflection('dirs', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('notPaths', $finder)); + $this->assertContains('vendor', $this->getPrivateValueFromReflection('notPaths', $finder)); + + $this->assertCount(0, $this->getPrivateValueFromReflection('filters', $finder)); + $this->assertCount(0, $this->getPrivateValueFromReflection('depths', $finder)); + $this->assertCount(0, $this->getPrivateValueFromReflection('sizes', $finder)); + } + + /** + * @return void + */ + public function testBuilder(): void + { + $finder = $this->builder + ->setDirectories([dirname(__DIR__)]) + ->setIncludeFiles([]) + ->setExcludes(['*cache*.php']) + ->setSuffixes(['php']) + ->setExcludeFiles([ 'src/AppKernel.php']) + ->setExcludePaths(['vendor']) + ->getFinder(); + + $this->assertCount(1, $this->getPrivateValueFromReflection('names', $finder)); + $this->assertContains('*.php', $this->getPrivateValueFromReflection('names', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('notNames', $finder)); + $this->assertContains('src/AppKernel.php', $this->getPrivateValueFromReflection('notNames', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('exclude', $finder)); + $this->assertContains('*cache*.php', $this->getPrivateValueFromReflection('exclude', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('dirs', $finder)); + $this->assertContains(dirname(__DIR__), $this->getPrivateValueFromReflection('dirs', $finder)); + + $this->assertCount(1, $this->getPrivateValueFromReflection('notPaths', $finder)); + $this->assertContains('vendor', $this->getPrivateValueFromReflection('notPaths', $finder)); + + $this->assertCount(0, $this->getPrivateValueFromReflection('filters', $finder)); + $this->assertCount(0, $this->getPrivateValueFromReflection('depths', $finder)); + $this->assertCount(0, $this->getPrivateValueFromReflection('sizes', $finder)); + } + + /** + * @param string $name + * @param Finder $finder + * + * @return array + * @throws \ReflectionException + */ + private function getPrivateValueFromReflection(string $name, Finder $finder): array + { + $property = (new \ReflectionClass(Finder::class))->getProperty($name); + $property->setAccessible(true); + + return $property->getValue($finder); + } +} diff --git a/tests/Unit/Builder/ProcessorBuilderTest.php b/tests/Unit/Builder/ProcessorBuilderTest.php new file mode 100644 index 0000000..89016ff --- /dev/null +++ b/tests/Unit/Builder/ProcessorBuilderTest.php @@ -0,0 +1,90 @@ +builder = new ProcessorBuilder(); + } + + /** + * @return void + */ + public function testBuilder(): void + { + $processors = $this->builder + ->setNames('SplitIsset') + ->getProcessors(); + + $this->assertCount(1, $processors); + } + + /** + * @return void + */ + public function testBuilderMultiple(): void + { + $visitors = $this->builder + ->setNames('SplitIsset,NegateBooleanNot') + ->getProcessors(); + + $this->assertCount(2, $visitors); + } + + /** + * @return void + */ + public function testBuilderMultipleWithSpace(): void + { + $visitors = $this->builder + ->setNames('SplitIsset, NegateBooleanNot') + ->getProcessors(); + + $this->assertCount(2, $visitors); + } + + /** + * @return void + */ + public function testBuilderWillThrowExceptionIfInvalidName(): void + { + $this->expectException(InvalidProcessorNameException::class); + $this->expectExceptionMessage( + 'Processor with name ThisNameWillNeverExist does not exist. Possible processor-names are: ' + ); + + $this->builder + ->setNames('SplitIsset, NegateBooleanNot, ThisNameWillNeverExist') + ->getProcessors(); + } + + /** + * @return void + */ + public function testBuilderWithEmptyName(): void + { + $visitors = $this->builder + ->setNames('') + ->getProcessors(); + + $this->assertCount(0, $visitors); + } +} diff --git a/tests/Unit/Builder/SortConfigurationBuilderTest.php b/tests/Unit/Builder/SortConfigurationBuilderTest.php new file mode 100644 index 0000000..9b269dc --- /dev/null +++ b/tests/Unit/Builder/SortConfigurationBuilderTest.php @@ -0,0 +1,148 @@ +builder = new SortConfigurationBuilder(); + } + + /** + * @return void + */ + public function testGetSortConfigurationWithOneField(): void + { + $sortConfiguration = $this->builder + ->addSortField('count', 'desc') + ->getSortConfiguration(); + + $this->assertInstanceOf(Sort::class, $sortConfiguration); + $this->assertNull($sortConfiguration->getFirstResult()); + $this->assertNull($sortConfiguration->getMaxResults()); + $this->assertCount(1, $sortConfiguration->getFields()); + $this->assertInstanceOf(SortField::class, $sortConfiguration->getFields()->first()); + $this->assertEquals('count', $sortConfiguration->getFields()->first()->getField()); + $this->assertEquals('DESC', $sortConfiguration->getFields()->first()->getDirection()); + } + + /** + * @return void + */ + public function testGetSortConfigurationWithTwoFields(): void + { + $sortConfiguration = $this->builder + ->addSortField('count', 'desc') + ->addSortField('name', 'asc') + ->getSortConfiguration(); + + $this->assertInstanceOf(Sort::class, $sortConfiguration); + $this->assertNull($sortConfiguration->getFirstResult()); + $this->assertNull($sortConfiguration->getMaxResults()); + $this->assertCount(2, $sortConfiguration->getFields()); + + /** @var SortField $field */ + foreach ($sortConfiguration->getFields() as $key => $field) { + $this->assertInstanceOf(SortField::class, $field); + if (0 === $key) { + $this->assertEquals('count', $field->getField()); + $this->assertEquals('DESC', $field->getDirection()); + } elseif (1 === $key) { + $this->assertEquals('name', $field->getField()); + $this->assertEquals('ASC', $field->getDirection()); + } + } + } + + /** + * @return void + */ + public function testGetSortConfigurationWithOneFieldAndMaximumEntries(): void + { + $sortConfiguration = $this->builder + ->setMaxResults(10) + ->addSortField('count', 'desc') + ->getSortConfiguration(); + + $this->assertInstanceOf(Sort::class, $sortConfiguration); + $this->assertEquals(10, $sortConfiguration->getMaxResults()); + $this->assertNull($sortConfiguration->getFirstResult()); + } + + /** + * @return void + */ + public function testGetSortConfigurationWithOneFieldAndFirstResult(): void + { + $sortConfiguration = $this->builder + ->setFirstResult(10) + ->addSortField('count', 'desc') + ->getSortConfiguration(); + + $this->assertInstanceOf(Sort::class, $sortConfiguration); + $this->assertEquals(10, $sortConfiguration->getFirstResult()); + $this->assertNull($sortConfiguration->getMaxResults()); + } + + /** + * @return void + */ + public function testGetSortConfigurationWithOneFieldAndFirstResultAndMaximumEntries(): void + { + $sortConfiguration = $this->builder + ->setFirstResult(10) + ->setMaxResults(10) + ->addSortField('count', 'desc') + ->getSortConfiguration(); + + $this->assertInstanceOf(Sort::class, $sortConfiguration); + $this->assertEquals(10, $sortConfiguration->getFirstResult()); + $this->assertEquals(10, $sortConfiguration->getMaxResults()); + } + + /** + * @return void + */ + public function testGetSortConfigurationWillThrowAnExceptionWhenTheSortDirectionOfOneFieldIsInvalid(): void + { + $this->expectException(InvalidSortArgumentException::class); + $this->expectExceptionMessage("Sort direction with 'foo' is invalid, use 'asc' or 'desc' instead"); + + $this->builder + ->addSortField('count', 'foo') + ->getSortConfiguration(); + } + + /** + * @return void + */ + public function testGetSortConfigurationWillThrowAnExceptionNoSortFieldsAreDeclared(): void + { + $this->expectException(InvalidSortConfigurationException::class); + $this->expectExceptionMessage('You need to add at least one sort field'); + + $this->builder + ->setMaxResults(10) + ->getSortConfiguration(); + } +} diff --git a/tests/Unit/Builder/VisitorBuilderTest.php b/tests/Unit/Builder/VisitorBuilderTest.php new file mode 100644 index 0000000..68ea8d6 --- /dev/null +++ b/tests/Unit/Builder/VisitorBuilderTest.php @@ -0,0 +1,90 @@ +builder = new VisitorBuilder(); + } + + /** + * @return void + */ + public function testBuilder(): void + { + $visitors = $this->builder + ->setNames('If') + ->getVisitors(); + + $this->assertCount(1, $visitors); + } + + /** + * @return void + */ + public function testBuilderMultiple(): void + { + $visitors = $this->builder + ->setNames('If,Ternary') + ->getVisitors(); + + $this->assertCount(2, $visitors); + } + + /** + * @return void + */ + public function testBuilderMultipleWithSpace(): void + { + $visitors = $this->builder + ->setNames('If, Ternary') + ->getVisitors(); + + $this->assertCount(2, $visitors); + } + + /** + * @return void + */ + public function testBuilderWillThrowExceptionIfInvalidName(): void + { + $this->expectException(InvalidVisitorNameException::class); + $this->expectExceptionMessage( + 'Visitor with name ThisNameWillNeverExist does not exist. Possible visitor-names are: ' + ); + + $this->builder + ->setNames('If, Ternary, ThisNameWillNeverExist') + ->getVisitors(); + } + + /** + * @return void + */ + public function testBuilderWithEmptyName(): void + { + $visitors = $this->builder + ->setNames('') + ->getVisitors(); + + $this->assertCount(0, $visitors); + } +} diff --git a/tests/Unit/Finder/FinderTest.php b/tests/Unit/Finder/FinderTest.php new file mode 100644 index 0000000..d2c6dd8 --- /dev/null +++ b/tests/Unit/Finder/FinderTest.php @@ -0,0 +1,40 @@ +finder = new Finder([dirname(__DIR__)], [], [], [], [], []); + } + + /** + * @return void + */ + public function testFinderDefaultParameters(): void + { + $property = (new \ReflectionClass(\Symfony\Component\Finder\Finder::class))->getProperty('ignore'); + $property->setAccessible(true); + $this->assertEquals(3, $property->getValue($this->finder)); + // 3 is the sum of the constants + // const IGNORE_VCS_FILES = 1; + // const IGNORE_DOT_FILES = 2; + } +} diff --git a/tests/Unit/Node/AbstractNodeTestCase.php b/tests/Unit/Node/AbstractNodeTestCase.php new file mode 100644 index 0000000..3c5f439 --- /dev/null +++ b/tests/Unit/Node/AbstractNodeTestCase.php @@ -0,0 +1,116 @@ +createSplFileInfoMock(); + + return new Occurrence($node, $splFileInfo); + } + + /** + * @return Occurrence + */ + protected function createFakeOccurrence(): Occurrence + { + /** @var SplFileInfo|MockObject $splFileInfo */ + $splFileInfo = $this->createSplFileInfoMock(); + + $node = $this->createMock(Node::class); + + return new Occurrence($node, $splFileInfo); + } + + /** + * @param string $name + * + * @return Node\Expr\Variable + */ + protected function createVariableNode(string $name = 'mockedVariableName'): Node\Expr\Variable + { + return new Node\Expr\Variable($name, $this->getNodeAttributes()); + } + + /** + * @param string $value + * + * @return Node\Scalar\String_ + */ + protected function createScalarStringNode(string $value): Node\Scalar\String_ + { + return new Node\Scalar\String_($value, $this->getNodeAttributes()); + } + + /** + * @param string $name + * + * @return Node\Name + */ + protected function createNameNode(string $name = 'mockedName'): Node\Name + { + return new Node\Name($name, $this->getNodeAttributes()); + } + + /** + * @param string $name + * + * @return Node\Identifier + */ + protected function createIdentifierNode(string $name = 'mockedName'): Node\Identifier + { + return new Node\Identifier($name, $this->getNodeAttributes()); + } + + /** + * @param Node\Expr $node + * @param bool $byRef + * @param bool $unpack + * + * @return Node\Arg + */ + protected function createArgNode(Node\Expr $node, $byRef = false, $unpack = false): Node\Arg + { + return new Node\Arg($node, $byRef, $unpack, $this->getNodeAttributes()); + } + + /** + * @return MockObject + */ + protected function createSplFileInfoMock(): MockObject + { + return $this->createMock(SplFileInfo::class); + } + + /** + * @param int $startLine + * @param int $endLine + * + * @return array + */ + protected function getNodeAttributes(int $startLine = 1, int $endLine = 1): array + { + return [ + 'startLine' => $startLine, + 'endLine' => $endLine, + ]; + } +} diff --git a/tests/Unit/Node/AbstractVisitorTest.php b/tests/Unit/Node/AbstractVisitorTest.php new file mode 100644 index 0000000..21c7037 --- /dev/null +++ b/tests/Unit/Node/AbstractVisitorTest.php @@ -0,0 +1,49 @@ +addNodeOccurrence(new Variable('test')); + } + }; + // phpcs:enable + + $this->assertCount(0, $visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + $visitor->setFile($file); + + $visitor->add(); + + $this->assertCount(1, $visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $visitor->getNodeOccurrenceList()->getOccurrences()->first(); + $this->assertEquals($file, $occurrence->getFile()); + $this->assertEquals(new Variable('test'), $occurrence->getNode()); + } +} diff --git a/tests/Unit/Node/ConditionList/CountableTest.php b/tests/Unit/Node/ConditionList/CountableTest.php new file mode 100644 index 0000000..5fab050 --- /dev/null +++ b/tests/Unit/Node/ConditionList/CountableTest.php @@ -0,0 +1,57 @@ +conditionList = new Countable(); + } + + /** + * @return void + */ + public function testAddCondition(): void + { + $this->assertCount(0, $this->conditionList->getCountedConditions()); + + $condition = new Condition('$x === $y', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(1, $this->conditionList->getCountedConditions()); + $this->assertCount(1, $this->conditionList->getCountedConditions()->first()->getOccurrences()); + + $condition = new Condition('$x === $y', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(1, $this->conditionList->getCountedConditions()); + $this->assertCount(2, $this->conditionList->getCountedConditions()->first()->getOccurrences()); + + $condition = new Condition('$a === $b', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(2, $this->conditionList->getCountedConditions()); + foreach ($this->conditionList->getCountedConditions() as $countedCondition) { + if ('$x === $y' === $countedCondition->getCondition()) { + $this->assertCount(2, $countedCondition->getOccurrences()); + } else { + $this->assertCount(1, $countedCondition->getOccurrences()); + } + } + } +} diff --git a/tests/Unit/Node/ConditionList/FlipCheckingTest.php b/tests/Unit/Node/ConditionList/FlipCheckingTest.php new file mode 100644 index 0000000..6e6da8c --- /dev/null +++ b/tests/Unit/Node/ConditionList/FlipCheckingTest.php @@ -0,0 +1,56 @@ +conditionList = new FlipChecking(); + } + + /** + * @return void + */ + public function testAddCondition(): void + { + $this->assertCount(0, $this->conditionList->getConditions()); + + $condition = new Condition('$x === $y', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(1, $this->conditionList->getConditions()); + $this->assertEquals('$x === $y', $this->conditionList->getConditions()[0]->getCondition()); + + + $condition = new Condition('$x === $a', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(2, $this->conditionList->getConditions()); + $this->assertEquals('$x === $y', $this->conditionList->getConditions()[0]->getCondition()); + $this->assertEquals('$x === $a', $this->conditionList->getConditions()[1]->getCondition()); + + // this will get flipped + $condition = new Condition('$y === $x', $this->createFakeOccurrence()); + $this->conditionList->addCondition($condition); + $this->assertCount(3, $this->conditionList->getConditions()); + $this->assertEquals('$x === $y', $this->conditionList->getConditions()[0]->getCondition()); + $this->assertEquals('$x === $a', $this->conditionList->getConditions()[1]->getCondition()); + $this->assertEquals('$x === $y', $this->conditionList->getConditions()[2]->getCondition()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/EqualTest.php b/tests/Unit/Node/Expr/BooleanNot/EqualTest.php new file mode 100644 index 0000000..2c21103 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/EqualTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\NotEqual::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/GreaterOrEqualTest.php b/tests/Unit/Node/Expr/BooleanNot/GreaterOrEqualTest.php new file mode 100644 index 0000000..20bfbed --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/GreaterOrEqualTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\SmallerOrEqual::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/GreaterTest.php b/tests/Unit/Node/Expr/BooleanNot/GreaterTest.php new file mode 100644 index 0000000..8a3e7b2 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/GreaterTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\Smaller::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/IdenticalTest.php b/tests/Unit/Node/Expr/BooleanNot/IdenticalTest.php new file mode 100644 index 0000000..24ef503 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/IdenticalTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\NotIdentical::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/NotEqualTest.php b/tests/Unit/Node/Expr/BooleanNot/NotEqualTest.php new file mode 100644 index 0000000..59b1412 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/NotEqualTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\Equal::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/NotIdenticalTest.php b/tests/Unit/Node/Expr/BooleanNot/NotIdenticalTest.php new file mode 100644 index 0000000..8db23a5 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/NotIdenticalTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\Identical::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/SmallerOrEqualTest.php b/tests/Unit/Node/Expr/BooleanNot/SmallerOrEqualTest.php new file mode 100644 index 0000000..8038172 --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/SmallerOrEqualTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\GreaterOrEqual::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Expr/BooleanNot/SmallerTest.php b/tests/Unit/Node/Expr/BooleanNot/SmallerTest.php new file mode 100644 index 0000000..d0038ed --- /dev/null +++ b/tests/Unit/Node/Expr/BooleanNot/SmallerTest.php @@ -0,0 +1,33 @@ +negate($node); + + $this->assertInstanceOf(BinaryOp\Greater::class, $negatedNode); + $this->assertEquals($node->left, $negatedNode->left); + $this->assertEquals($node->right, $negatedNode->right); + $this->assertEquals($node->getAttributes(), $negatedNode->getAttributes()); + } +} diff --git a/tests/Unit/Node/Processor/NegateBooleanNotProcessorTest.php b/tests/Unit/Node/Processor/NegateBooleanNotProcessorTest.php new file mode 100644 index 0000000..ad37748 --- /dev/null +++ b/tests/Unit/Node/Processor/NegateBooleanNotProcessorTest.php @@ -0,0 +1,221 @@ +processor = new NegateBooleanNotProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new BooleanNot( + new Identical( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(NotIdentical::class, $occurrence->getNode()); + $this->assertContains('NegateBooleanNot', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessNested(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new BooleanNot( + new BooleanAnd( + new Identical( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + new Identical( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(BooleanAnd::class, $occurrence->getNode()); + $this->assertInstanceOf(NotIdentical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(NotIdentical::class, $occurrence->getNode()->right); + $this->assertContains('NegateBooleanNot', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessNestedBooleanNot(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new BooleanNot( + new BooleanAnd( + new BooleanNot( + new Identical( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Identical( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(BooleanAnd::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(NotIdentical::class, $occurrence->getNode()->right); + $this->assertContains('NegateBooleanNot', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/RemoveAssignmentProcessorTest.php b/tests/Unit/Node/Processor/RemoveAssignmentProcessorTest.php new file mode 100644 index 0000000..34950da --- /dev/null +++ b/tests/Unit/Node/Processor/RemoveAssignmentProcessorTest.php @@ -0,0 +1,226 @@ +processor = new RemoveAssignmentProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $node = new Assign( + $this->createVariableNode('x'), + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->right); + $this->assertContains('RemoveAssignment', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOp(): void + { + $node = new Identical( + new Assign( + $this->createVariableNode('x'), + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'y', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->right); + $this->assertContains('RemoveAssignment', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOpNested(): void + { + $node = new BooleanAnd( + new Identical( + new Assign( + $this->createVariableNode('x'), + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'y', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Identical( + new Variable( + 'x', + $this->getNodeAttributes() + ), + new Variable( + 'x', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(BooleanAnd::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left->right); + $this->assertContains('RemoveAssignment', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/RemoveCastProcessorTest.php b/tests/Unit/Node/Processor/RemoveCastProcessorTest.php new file mode 100644 index 0000000..660f618 --- /dev/null +++ b/tests/Unit/Node/Processor/RemoveCastProcessorTest.php @@ -0,0 +1,225 @@ +processor = new RemoveCastProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $node = new Bool_( + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->right); + $this->assertContains('RemoveCast', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOp(): void + { + $node = new Identical( + new Int_( + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'y', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->right); + $this->assertContains('RemoveCast', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOpNested(): void + { + $node = new BooleanAnd( + new Identical( + new String_( + new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'y', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Identical( + new Variable( + 'x', + $this->getNodeAttributes() + ), + new Variable( + 'x', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(BooleanAnd::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left->left); + $this->assertInstanceOf(Variable::class, $occurrence->getNode()->left->left->right); + $this->assertContains('RemoveCast', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/RemoveDuplicateBooleanNotProcessorTest.php b/tests/Unit/Node/Processor/RemoveDuplicateBooleanNotProcessorTest.php new file mode 100644 index 0000000..fdbd4c1 --- /dev/null +++ b/tests/Unit/Node/Processor/RemoveDuplicateBooleanNotProcessorTest.php @@ -0,0 +1,121 @@ +processor = new RemoveDuplicateBooleanNotProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $node = new BooleanNot( + new BooleanNot( + new Identical( + $this->createVariableNode('x'), + $this->createVariableNode('y'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertContains('RemoveDuplicateBooleanNot', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/RemoveSingleFullyQualifiedNameProcessorTest.php b/tests/Unit/Node/Processor/RemoveSingleFullyQualifiedNameProcessorTest.php new file mode 100644 index 0000000..7ff95b6 --- /dev/null +++ b/tests/Unit/Node/Processor/RemoveSingleFullyQualifiedNameProcessorTest.php @@ -0,0 +1,219 @@ +processor = new RemoveSingleFullyQualifiedNameProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $node = new ConstFetch( + new FullyQualified( + 'test', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(ConstFetch::class, $occurrence->getNode()); + $this->assertInstanceOf(Name::class, $occurrence->getNode()->name); + $this->assertEquals('test', (string) $occurrence->getNode()->name); + $this->assertContains('RemoveSingleFullyQualifiedName', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOp(): void + { + $node = new Identical( + new ConstFetch( + new FullyQualified( + 'test', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'x', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(Identical::class, $occurrence->getNode()); + $this->assertInstanceOf(ConstFetch::class, $occurrence->getNode()->left); + $this->assertInstanceOf(Name::class, $occurrence->getNode()->left->name); + $this->assertEquals('test', (string) $occurrence->getNode()->left->name); + $this->assertContains('RemoveSingleFullyQualifiedName', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessBinaryOpNested(): void + { + $node = new BooleanAnd( + new Identical( + new ConstFetch( + new FullyQualified( + 'test', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Variable( + 'x', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + new Identical( + new Variable( + 'x', + $this->getNodeAttributes() + ), + new Variable( + 'x', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertInstanceOf(BooleanAnd::class, $occurrence->getNode()); + $this->assertInstanceOf(Identical::class, $occurrence->getNode()->left); + $this->assertInstanceOf(ConstFetch::class, $occurrence->getNode()->left->left); + $this->assertInstanceOf(Name::class, $occurrence->getNode()->left->left->name); + $this->assertEquals('test', (string) $occurrence->getNode()->left->left->name); + $this->assertContains('RemoveSingleFullyQualifiedName', $occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/SplitIssetProcessorTest.php b/tests/Unit/Node/Processor/SplitIssetProcessorTest.php new file mode 100644 index 0000000..e92b075 --- /dev/null +++ b/tests/Unit/Node/Processor/SplitIssetProcessorTest.php @@ -0,0 +1,148 @@ +processor = new SplitIssetProcessor(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new Isset_($childNodes, $this->getNodeAttributes()); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(2, $nodeOccurrenceList->getOccurrences()); + + foreach (array_values($nodeOccurrenceList->getOccurrences()->toArray()) as $key => $occurrence) { + $this->assertInstanceOf(Isset_::class, $occurrence->getNode()); + $this->assertCount(1, $occurrence->getNode()->vars); + $this->assertEquals($childNodes[$key], $occurrence->getNode()->vars[0]); + $this->assertContains('SplitIsset', $occurrence->getAffectedByProcessors()); + } + } + + /** + * @return void + */ + public function testProcessBooleanNot(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new BooleanNot(new Isset_($childNodes, $this->getNodeAttributes()), $this->getNodeAttributes()); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(2, $nodeOccurrenceList->getOccurrences()); + + foreach (array_values($nodeOccurrenceList->getOccurrences()->toArray()) as $key => $occurrence) { + $this->assertInstanceOf(BooleanNot::class, $occurrence->getNode()); + $this->assertInstanceOf(Isset_::class, $occurrence->getNode()->expr); + $this->assertCount(1, $occurrence->getNode()->expr->vars); + $this->assertEquals($childNodes[$key], $occurrence->getNode()->expr->vars[0]); + $this->assertContains('SplitIsset', $occurrence->getAffectedByProcessors()); + } + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/Processor/SplitLogicalOperatorProcessorTest.php b/tests/Unit/Node/Processor/SplitLogicalOperatorProcessorTest.php new file mode 100644 index 0000000..83d9ad2 --- /dev/null +++ b/tests/Unit/Node/Processor/SplitLogicalOperatorProcessorTest.php @@ -0,0 +1,213 @@ +processor = new SplitLogicalOperatorProcessor(); + } + + /** + * @return array + */ + public function logicalOperatorProvider(): array + { + return [ + '&&' => [BooleanAnd::class], + '||' => [BooleanOr::class], + 'and' => [LogicalAnd::class], + 'or' => [LogicalOr::class], + ]; + } + + /** + * @return void + * + * @dataProvider logicalOperatorProvider + */ + public function testProcess(string $logicalProviderClass): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new $logicalProviderClass($childNodes[0], $childNodes[1], $this->getNodeAttributes()); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(2, $nodeOccurrenceList->getOccurrences()); + + foreach (array_values($nodeOccurrenceList->getOccurrences()->toArray()) as $key => $occurrence) { + $this->assertEquals($childNodes[$key], $occurrence->getNode()); + $this->assertContains('SplitLogicalOperator', $occurrence->getAffectedByProcessors()); + } + } + + /** + * @return void + */ + public function testProcessNested(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->createVariableNode('c'), + $this->createVariableNode('d'), + ]; + $node = new BooleanAnd( + new BooleanAnd( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + new BooleanOr( + $childNodes[2], + $childNodes[3], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(4, $nodeOccurrenceList->getOccurrences()); + + foreach (array_values($nodeOccurrenceList->getOccurrences()->toArray()) as $key => $occurrence) { + $this->assertEquals($childNodes[$key], $occurrence->getNode()); + $this->assertContains('SplitLogicalOperator', $occurrence->getAffectedByProcessors()); + } + } + + /** + * @return void + */ + public function testProcessBooleanNot(): void + { + $childNodes = [ + $this->createVariableNode('a'), + $this->createVariableNode('b'), + ]; + $node = new BooleanNot( + new BooleanAnd( + $childNodes[0], + $childNodes[1], + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + $this->assertCount(2, $nodeOccurrenceList->getOccurrences()); + + foreach (array_values($nodeOccurrenceList->getOccurrences()->toArray()) as $key => $occurrence) { + $this->assertInstanceOf(BooleanNot::class, $occurrence->getNode()); + $this->assertEquals($childNodes[$key], $occurrence->getNode()->expr); + $this->assertContains('SplitLogicalOperator', $occurrence->getAffectedByProcessors()); + } + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodes(): void + { + $node = $this->createVariableNode('a'); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } + + /** + * @return void + */ + public function testProcessWillNotAffectedWrongNodesWithBinaryOp(): void + { + $node = new Identical( + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + $occurrence = $this->createOccurrence($node); + + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($occurrence); + $this->processor->setNodeOccurrenceList($nodeOccurrenceList); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + + $this->processor->process($occurrence); + + /** @var Occurrence $occurrence */ + $occurrence = $nodeOccurrenceList->getOccurrences()->first(); + + $this->assertCount(1, $nodeOccurrenceList->getOccurrences()); + $this->assertEmpty($occurrence->getAffectedByProcessors()); + } +} diff --git a/tests/Unit/Node/ProcessorRunnerTest.php b/tests/Unit/Node/ProcessorRunnerTest.php new file mode 100644 index 0000000..38f2966 --- /dev/null +++ b/tests/Unit/Node/ProcessorRunnerTest.php @@ -0,0 +1,56 @@ +processorRunner = new ProcessorRunner(); + } + + /** + * @return void + */ + public function testProcess(): void + { + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence($this->createOccurrence($this->createVariableNode('var1'))); + $nodeOccurrenceList->addOccurrence($this->createOccurrence($this->createVariableNode('var2'))); + $nodeOccurrenceList->addOccurrence($this->createOccurrence($this->createVariableNode('var3'))); + + $childProcessor = $this->createMock(ProcessorInterface::class); + $childProcessor + ->expects($this->once()) + ->method('setNodeOccurrenceList') + ->with($nodeOccurrenceList); + $childProcessor + ->expects($this->exactly(3)) + ->method('process'); + + $this->processorRunner->addProcessor($childProcessor); + + $processorsDoneCounter = 1; + foreach ($this->processorRunner->process($nodeOccurrenceList) as $processorsDone) { + $this->assertEquals($processorsDoneCounter, $processorsDone); + + $processorsDoneCounter++; + } + } +} diff --git a/tests/Unit/Node/Representation/AbstractNodeRepresentationTest.php b/tests/Unit/Node/Representation/AbstractNodeRepresentationTest.php new file mode 100644 index 0000000..6b69367 --- /dev/null +++ b/tests/Unit/Node/Representation/AbstractNodeRepresentationTest.php @@ -0,0 +1,27 @@ +nodeRepresentationService = $this->createMock(NodeRepresentationService::class); + } +} diff --git a/tests/Unit/Node/Representation/ArgTest.php b/tests/Unit/Node/Representation/ArgTest.php new file mode 100644 index 0000000..c0a7491 --- /dev/null +++ b/tests/Unit/Node/Representation/ArgTest.php @@ -0,0 +1,68 @@ + ['$value', $this->createVariableNode('value'), false, false], + 'byRef' => ['&$value', $this->createVariableNode('value'), true, false], + 'unpack' => ['...$items', $this->createVariableNode('items'), false, true], + 'byRef unpack' => ['...&$items', $this->createVariableNode('items'), true, true], + ]; + } + + /** + * @param string $expectedOutput + * @param Node\Expr $value + * @param bool $byRef + * @param bool $unpack + * + * @return void + * + * @dataProvider argProvider + */ + public function testGetRepresentation(string $expectedOutput, Node\Expr $value, bool $byRef, bool $unpack): void + { + $node = new Node\Arg( + $value, + $byRef, + $unpack, + $this->getNodeAttributes() + ); + + if ($unpack) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$items'); + } else { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$value'); + } + + $representation = new Arg($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ArrayDimFetchTest.php b/tests/Unit/Node/Representation/Expr/ArrayDimFetchTest.php new file mode 100644 index 0000000..867bb4c --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ArrayDimFetchTest.php @@ -0,0 +1,62 @@ +createVariableNode('test'), + $this->createScalarStringNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$test', "'x'"); + + $representation = new ArrayDimFetch($this->nodeRepresentationService, $node); + + $this->assertEquals("\$test['x']", $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithEmptyDim(): void + { + $node = new Node\Expr\ArrayDimFetch( + $this->createVariableNode('test'), + null, + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$test'); + + $representation = new ArrayDimFetch($this->nodeRepresentationService, $node); + + $this->assertEquals('$test[]', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ArrayItemTest.php b/tests/Unit/Node/Representation/Expr/ArrayItemTest.php new file mode 100644 index 0000000..fe0c679 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ArrayItemTest.php @@ -0,0 +1,75 @@ + ['$value', $this->createVariableNode('value'), null, false, false], + 'byRef' => ['&$value', $this->createVariableNode('value'), null, true, false], + 'simple + key' => ["'key' => \$value", $this->createVariableNode('value'), $this->createScalarStringNode('key'), false, false], + 'unpack' => ['...$items', $this->createVariableNode('items'), null, false, true], + ]; + } + + /** + * @param string $expectedOutput + * @param Node\Expr $value + * @param Node\Expr|null $key + * @param bool $byRef + * @param bool $unpack + * + * @return void + * + * @dataProvider arrayItemProvider + */ + public function testGetRepresentation(string $expectedOutput, Node\Expr $value, ?Node\Expr $key, bool $byRef, bool $unpack): void + { + $node = new Node\Expr\ArrayItem( + $value, + $key, + $byRef, + $this->getNodeAttributes(), + $unpack + ); + + if ($unpack) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$items'); + } elseif (null !== $key) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn("'key'", '$value'); + } else { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$value'); + } + + $representation = new ArrayItem($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Array_Test.php b/tests/Unit/Node/Representation/Expr/Array_Test.php new file mode 100644 index 0000000..a6e90ad --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Array_Test.php @@ -0,0 +1,43 @@ +createVariableNode('test'), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$test']); + + $representation = new Array_($this->nodeRepresentationService, $node); + + $this->assertEquals('[$test]', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ArrowFunctionTest.php b/tests/Unit/Node/Representation/Expr/ArrowFunctionTest.php new file mode 100644 index 0000000..920eb93 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ArrowFunctionTest.php @@ -0,0 +1,102 @@ + ['(fn() => 1)', [], null, false, false], + 'returnType' => ['(fn(): int => 1)', [], 'int', false, false], + 'static' => ['(static fn() => 1)', [], null, true, false], + 'byRef' => ['(fn&() => 1)', [], null, false, true], + 'static byRef' => ['(static fn&() => 1)', [], null, true, true], + 'static byRef returnType' => ['(static fn&(): int => 1)', [], 'int', true, true], + 'returnType params' => [ + '(fn(int $number): int => $number)', + [ + new Node\Param( + $this->createVariableNode('number'), + null, + $this->createIdentifierNode('int'), + false, + false, + $this->getNodeAttributes() + ), + ], + 'int', + false, + false, + ], + ]; + } + + /** + * @param string $expectedOutput + * @param array $params + * @param string|null $returnType + * @param bool $static + * @param bool $byRef + * + * @return void + * + * @dataProvider arrowFunctionProvider + */ + public function testGetRepresentation(string $expectedOutput, array $params, ?string $returnType, bool $static, bool $byRef): void + { + $node = new Node\Expr\ArrowFunction( + [ + 'static' => $static, + 'byRef' => $byRef, + 'params' => $params, + 'returnType' => $returnType, + 'expr' => new Node\Scalar\LNumber(1, $this->getNodeAttributes()), + ], + $this->getNodeAttributes() + ); + + if (null === $returnType) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('1'); + } elseif (count($params)) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn($returnType, '$number'); + } else { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn($returnType, '1'); + } + + if (count($params)) { + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['int $number']); + } + + $representation = new ArrowFunction($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseAndTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseAndTest.php new file mode 100644 index 0000000..444625e --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseAndTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new BitwiseAnd($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable &= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseOrTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseOrTest.php new file mode 100644 index 0000000..ea2c740 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseOrTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new BitwiseOr($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable |= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseXorTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseXorTest.php new file mode 100644 index 0000000..46ed9fa --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/BitwiseXorTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new BitwiseXor($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable ^= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/CoalesceTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/CoalesceTest.php new file mode 100644 index 0000000..5e72af1 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/CoalesceTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Coalesce($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable ??= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/ConcatTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/ConcatTest.php new file mode 100644 index 0000000..f917139 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/ConcatTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Concat($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable .= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/DivTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/DivTest.php new file mode 100644 index 0000000..295dffe --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/DivTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Div($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable /= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/MinusTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/MinusTest.php new file mode 100644 index 0000000..de65640 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/MinusTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Minus($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable -= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/ModTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/ModTest.php new file mode 100644 index 0000000..d8bee02 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/ModTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Mod($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable %= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/MulTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/MulTest.php new file mode 100644 index 0000000..6399a7b --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/MulTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Mul($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable *= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/PlusTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/PlusTest.php new file mode 100644 index 0000000..86df867 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/PlusTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Plus($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable += $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/PowTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/PowTest.php new file mode 100644 index 0000000..6d20858 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/PowTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Pow($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable **= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/ShiftLeftTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/ShiftLeftTest.php new file mode 100644 index 0000000..99825cc --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/ShiftLeftTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new ShiftLeft($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable <<= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignOp/ShiftRightTest.php b/tests/Unit/Node/Representation/Expr/AssignOp/ShiftRightTest.php new file mode 100644 index 0000000..7135e52 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignOp/ShiftRightTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new ShiftRight($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable >>= $variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignRefTest.php b/tests/Unit/Node/Representation/Expr/AssignRefTest.php new file mode 100644 index 0000000..c0aa54e --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignRefTest.php @@ -0,0 +1,42 @@ +createVariableNode('x'), + $this->createVariableNode('y'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x', '$y'); + + $representation = new AssignRef($this->nodeRepresentationService, $node); + + $this->assertEquals('$x =& $y', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/AssignTest.php b/tests/Unit/Node/Representation/Expr/AssignTest.php new file mode 100644 index 0000000..bd82aa8 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/AssignTest.php @@ -0,0 +1,42 @@ +createVariableNode('x'), + $this->createVariableNode('y'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x', '$y'); + + $representation = new Assign($this->nodeRepresentationService, $node); + + $this->assertEquals('$x = $y', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/BinaryOp/ConcatTest.php b/tests/Unit/Node/Representation/Expr/BinaryOp/ConcatTest.php new file mode 100644 index 0000000..09805fb --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/BinaryOp/ConcatTest.php @@ -0,0 +1,42 @@ +createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new Concat($this->nodeRepresentationService, $node); + + $this->assertEquals('$variable.$variable2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/BinaryOpTest.php b/tests/Unit/Node/Representation/Expr/BinaryOpTest.php new file mode 100644 index 0000000..2f944c9 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/BinaryOpTest.php @@ -0,0 +1,106 @@ + ['$variable & $variable2', Node\Expr\BinaryOp\BitwiseAnd::class], + 'bitwiseOr' => ['$variable | $variable2', Node\Expr\BinaryOp\BitwiseOr::class], + 'bitwiseXor' => ['$variable ^ $variable2', Node\Expr\BinaryOp\BitwiseXor::class], + 'booleanAnd' => ['$variable && $variable2', Node\Expr\BinaryOp\BooleanAnd::class], + 'booleanOr' => ['$variable || $variable2', Node\Expr\BinaryOp\BooleanOr::class], + 'coalesce' => ['$variable ?? $variable2', Node\Expr\BinaryOp\Coalesce::class], + 'div' => ['$variable / $variable2', Node\Expr\BinaryOp\Div::class], + 'equal' => ['$variable == $variable2', Node\Expr\BinaryOp\Equal::class], + 'greater' => ['$variable > $variable2', Node\Expr\BinaryOp\Greater::class], + 'greaterOrEqual' => ['$variable >= $variable2', Node\Expr\BinaryOp\GreaterOrEqual::class], + 'identical' => ['$variable === $variable2', Node\Expr\BinaryOp\Identical::class], + 'logicalAnd' => ['$variable and $variable2', Node\Expr\BinaryOp\LogicalAnd::class], + 'logicalOr' => ['$variable or $variable2', Node\Expr\BinaryOp\LogicalOr::class], + 'logicalXor' => ['$variable xor $variable2', Node\Expr\BinaryOp\LogicalXor::class], + 'minus' => ['$variable - $variable2', Node\Expr\BinaryOp\Minus::class], + 'plus' => ['$variable + $variable2', Node\Expr\BinaryOp\Plus::class], + 'pow' => ['$variable ** $variable2', Node\Expr\BinaryOp\Pow::class], + 'shiftLeft' => ['$variable << $variable2', Node\Expr\BinaryOp\ShiftLeft::class], + 'shiftRight' => ['$variable >> $variable2', Node\Expr\BinaryOp\ShiftRight::class], + 'smaller' => ['$variable < $variable2', Node\Expr\BinaryOp\Smaller::class], + 'smallerOrEqual' => ['$variable <= $variable2', Node\Expr\BinaryOp\SmallerOrEqual::class], + 'spaceship' => ['$variable <=> $variable2', Node\Expr\BinaryOp\Spaceship::class], + ]; + } + + /** + * @param string $expectedOutput + * @param string $nodeClassname + * + * @return void + * + * @dataProvider binaryOpProvider + */ + public function testGetRepresentation(string $expectedOutput, string $nodeClassname): void + { + $node = new $nodeClassname( + $this->createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable', '$variable2'); + + $representation = new BinaryOp($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationForEncapsuledBooleanOr(): void + { + $node = new Node\Expr\BinaryOp\BooleanOr( + new Node\Expr\BinaryOp\Identical( + $this->createVariableNode('variable'), + $this->createVariableNode('variable2'), + $this->getNodeAttributes() + ), + new Node\Expr\BinaryOp\Identical( + $this->createVariableNode('variable3'), + $this->createVariableNode('variable4'), + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable === $variable2', '$variable3 === $variable4'); + + $representation = new BinaryOp($this->nodeRepresentationService, $node); + + $this->assertEquals('($variable === $variable2 || $variable3 === $variable4)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/BitwiseNotTest.php b/tests/Unit/Node/Representation/Expr/BitwiseNotTest.php new file mode 100644 index 0000000..fa40d9b --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/BitwiseNotTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new BitwiseNot($this->nodeRepresentationService, $node); + + $this->assertEquals('~$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/BooleanNotTest.php b/tests/Unit/Node/Representation/Expr/BooleanNotTest.php new file mode 100644 index 0000000..042d5c0 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/BooleanNotTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new BooleanNot($this->nodeRepresentationService, $node); + + $this->assertEquals('!$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Cast/DoubleTest.php b/tests/Unit/Node/Representation/Expr/Cast/DoubleTest.php new file mode 100644 index 0000000..303fd45 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Cast/DoubleTest.php @@ -0,0 +1,63 @@ + ['double', Node\Expr\Cast\Double::KIND_DOUBLE], + 'float' => ['float', Node\Expr\Cast\Double::KIND_FLOAT], + 'real' => ['real', Node\Expr\Cast\Double::KIND_REAL], + ]; + } + + /** + * @param string $expectedType + * @param string $nodeClassname + * + * @return void + * + * @dataProvider castProvider + */ + public function testGetRepresentation(string $expectedType, int $doubleType): void + { + $node = new Node\Expr\Cast\Double( + $this->createVariableNode('variable'), + array_merge( + [ + 'kind' => $doubleType, + ], + $this->getNodeAttributes() + ) + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable'); + + $representation = new Double($this->nodeRepresentationService, $node); + + $this->assertEquals(sprintf('(%s) $variable', $expectedType), $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/CastTest.php b/tests/Unit/Node/Representation/Expr/CastTest.php new file mode 100644 index 0000000..738e922 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/CastTest.php @@ -0,0 +1,62 @@ + ['int', Node\Expr\Cast\Int_::class, ], + 'double' => ['double', Node\Expr\Cast\Double::class], + 'bool' => ['bool', Node\Expr\Cast\Bool_::class], + 'array' => ['array', Node\Expr\Cast\Array_::class], + 'object' => ['object', Node\Expr\Cast\Object_::class], + 'string' => ['string', Node\Expr\Cast\String_::class], + 'unset' => ['unset', Node\Expr\Cast\Unset_::class], + ]; + } + + /** + * @param string $expectedType + * @param string $nodeClassname + * + * @return void + * + * @dataProvider castProvider + */ + public function testGetRepresentation(string $expectedType, string $nodeClassname): void + { + $node = new $nodeClassname( + $this->createVariableNode('variable'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$variable'); + + $representation = new Cast($this->nodeRepresentationService, $node); + + $this->assertEquals(sprintf('(%s) $variable', $expectedType), $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ClassConstFetchTest.php b/tests/Unit/Node/Representation/Expr/ClassConstFetchTest.php new file mode 100644 index 0000000..e5646da --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ClassConstFetchTest.php @@ -0,0 +1,42 @@ +createNameNode('self'), + 'X', + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('self', 'X'); + + $representation = new ClassConstFetch($this->nodeRepresentationService, $node); + + $this->assertEquals('self::X', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Clone_Test.php b/tests/Unit/Node/Representation/Expr/Clone_Test.php new file mode 100644 index 0000000..e8d4a27 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Clone_Test.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new Clone_($this->nodeRepresentationService, $node); + + $this->assertEquals('clone $x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ClosureTest.php b/tests/Unit/Node/Representation/Expr/ClosureTest.php new file mode 100644 index 0000000..29f23c5 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ClosureTest.php @@ -0,0 +1,143 @@ + ['function() { /* CLOSURE */ }', [], [], false, false], + 'static' => ['static function() { /* CLOSURE */ }', [], [], true, false], + 'byRef' => ['function&() { /* CLOSURE */ }', [], [], false, true], + 'static byRef' => ['static function&() { /* CLOSURE */ }', [], [], true, true], + ]; + } + + /** + * @param string $expectedOutput + * @param array $params + * @param array $uses + * @param bool $static + * @param bool $byRef + * + * @return void + * + * @dataProvider closureProvider + */ + public function testGetRepresentation(string $expectedOutput, array $params, array $uses, bool $static, bool $byRef): void + { + $node = new Node\Expr\Closure( + [ + 'static' => $static, + 'byRef' => $byRef, + 'params' => $params, + 'uses' => $uses, + ], + $this->getNodeAttributes() + ); + + $representation = new Closure($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithParams(): void + { + $node = new Node\Expr\Closure( + [ + 'static' => false, + 'byRef' => false, + 'params' => [ + $this->createVariableNode('test'), + ], + 'uses' => [], + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$test']); + + $representation = new Closure($this->nodeRepresentationService, $node); + + $this->assertEquals('function($test) { /* CLOSURE */ }', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithUses(): void + { + $node = new Node\Expr\Closure( + [ + 'static' => false, + 'byRef' => false, + 'params' => [], + 'uses' => [ + $this->createVariableNode('test'), + ], + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$test'],[]); + + $representation = new Closure($this->nodeRepresentationService, $node); + + $this->assertEquals('function() use ($test) { /* CLOSURE */ }', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithParamsAndUses(): void + { + $node = new Node\Expr\Closure( + [ + 'static' => false, + 'byRef' => true, + 'params' => [ + $this->createVariableNode('test2'), + ], + 'uses' => [ + $this->createVariableNode('test'), + ], + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$test'],['$test2']); + + $representation = new Closure($this->nodeRepresentationService, $node); + + $this->assertEquals('function&($test2) use ($test) { /* CLOSURE */ }', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ClosureUseTest.php b/tests/Unit/Node/Representation/Expr/ClosureUseTest.php new file mode 100644 index 0000000..f224973 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ClosureUseTest.php @@ -0,0 +1,62 @@ +createVariableNode('x'), + false, + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new ClosureUse($this->nodeRepresentationService, $node); + + $this->assertEquals('$x', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationByRef(): void + { + $node = new Node\Expr\ClosureUse( + $this->createVariableNode('x'), + true, + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new ClosureUse($this->nodeRepresentationService, $node); + + $this->assertEquals('&$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ConstFetchTest.php b/tests/Unit/Node/Representation/Expr/ConstFetchTest.php new file mode 100644 index 0000000..ccd5354 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ConstFetchTest.php @@ -0,0 +1,41 @@ +createNameNode('self'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('self'); + + $representation = new ConstFetch($this->nodeRepresentationService, $node); + + $this->assertEquals('self', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Empty_Test.php b/tests/Unit/Node/Representation/Expr/Empty_Test.php new file mode 100644 index 0000000..bc94593 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Empty_Test.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new Empty_($this->nodeRepresentationService, $node); + + $this->assertEquals('empty($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ErrorSuppressTest.php b/tests/Unit/Node/Representation/Expr/ErrorSuppressTest.php new file mode 100644 index 0000000..dc5bb55 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ErrorSuppressTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new ErrorSuppress($this->nodeRepresentationService, $node); + + $this->assertEquals('@$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Eval_Test.php b/tests/Unit/Node/Representation/Expr/Eval_Test.php new file mode 100644 index 0000000..8fe2ca7 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Eval_Test.php @@ -0,0 +1,41 @@ +createScalarStringNode('return 1337;'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn("'return 1337;'"); + + $representation = new Eval_($this->nodeRepresentationService, $node); + + $this->assertEquals("eval('return 1337;')", $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Exit_Test.php b/tests/Unit/Node/Representation/Expr/Exit_Test.php new file mode 100644 index 0000000..50b2b9a --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Exit_Test.php @@ -0,0 +1,56 @@ +getNodeAttributes()), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('1'); + + $representation = new Exit_($this->nodeRepresentationService, $node); + + $this->assertEquals('exit(1)', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithoutExpr(): void + { + $node = new Node\Expr\Exit_( + null, + $this->getNodeAttributes() + ); + + $representation = new Exit_($this->nodeRepresentationService, $node); + + $this->assertEquals('exit', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/FuncCallTest.php b/tests/Unit/Node/Representation/Expr/FuncCallTest.php new file mode 100644 index 0000000..f21c87b --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/FuncCallTest.php @@ -0,0 +1,47 @@ +createVariableNode('strtolower'), + [ + $this->createArgNode($this->createVariableNode('x')), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('strtolower'); + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new FuncCall($this->nodeRepresentationService, $node); + + $this->assertEquals('strtolower($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Include_Test.php b/tests/Unit/Node/Representation/Expr/Include_Test.php new file mode 100644 index 0000000..4cbfa64 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Include_Test.php @@ -0,0 +1,60 @@ + [Node\Expr\Include_::TYPE_INCLUDE, 'include'], + 'include_once' => [Node\Expr\Include_::TYPE_INCLUDE_ONCE, 'include_once'], + 'require' => [Node\Expr\Include_::TYPE_REQUIRE, 'require'], + 'require_once' => [Node\Expr\Include_::TYPE_REQUIRE_ONCE, 'require_once'], + ]; + } + + /** + * @param int $type + * @param string $includeFunction + * + * @return void + * + * @dataProvider includeProvider + */ + public function testGetRepresentation(int $type, string $includeFunction): void + { + $node = new Node\Expr\Include_( + new Node\Scalar\String_('path/to/file'), + $type, + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn("'path/to/file'"); + + $representation = new Include_($this->nodeRepresentationService, $node); + + $this->assertEquals(sprintf("%s('path/to/file')", $includeFunction), $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Instanceof_Test.php b/tests/Unit/Node/Representation/Expr/Instanceof_Test.php new file mode 100644 index 0000000..ec33a73 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Instanceof_Test.php @@ -0,0 +1,42 @@ +createVariableNode('classObject'), + $this->createNameNode('test'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$classObject', 'test'); + + $representation = new Instanceof_($this->nodeRepresentationService, $node); + + $this->assertEquals('$classObject instanceof test', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Isset_Test.php b/tests/Unit/Node/Representation/Expr/Isset_Test.php new file mode 100644 index 0000000..149c4b4 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Isset_Test.php @@ -0,0 +1,43 @@ +createVariableNode('x'), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new Isset_($this->nodeRepresentationService, $node); + + $this->assertEquals('isset($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/List_Test.php b/tests/Unit/Node/Representation/Expr/List_Test.php new file mode 100644 index 0000000..9dff290 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/List_Test.php @@ -0,0 +1,43 @@ +createArgNode($this->createVariableNode('x')), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new List_($this->nodeRepresentationService, $node); + + $this->assertEquals('list($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/MethodCallTest.php b/tests/Unit/Node/Representation/Expr/MethodCallTest.php new file mode 100644 index 0000000..d30a3f6 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/MethodCallTest.php @@ -0,0 +1,48 @@ +createVariableNode('classObject'), + $this->createNameNode('test'), + [ + $this->createArgNode($this->createVariableNode('x')), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$classObject', 'test'); + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new MethodCall($this->nodeRepresentationService, $node); + + $this->assertEquals('$classObject->test($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/New_Test.php b/tests/Unit/Node/Representation/Expr/New_Test.php new file mode 100644 index 0000000..cbe5443 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/New_Test.php @@ -0,0 +1,47 @@ +createNameNode('Classname'), + [ + $this->createArgNode($this->createVariableNode('x')), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('Classname'); + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new New_($this->nodeRepresentationService, $node); + + $this->assertEquals('(new Classname($x))', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/PostDecTest.php b/tests/Unit/Node/Representation/Expr/PostDecTest.php new file mode 100644 index 0000000..69e6538 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/PostDecTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new PostDec($this->nodeRepresentationService, $node); + + $this->assertEquals('$x--', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/PostIncTest.php b/tests/Unit/Node/Representation/Expr/PostIncTest.php new file mode 100644 index 0000000..4e77e2d --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/PostIncTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new PostInc($this->nodeRepresentationService, $node); + + $this->assertEquals('$x++', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/PreDecTest.php b/tests/Unit/Node/Representation/Expr/PreDecTest.php new file mode 100644 index 0000000..751f6f5 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/PreDecTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new PreDec($this->nodeRepresentationService, $node); + + $this->assertEquals('--$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/PreIncTest.php b/tests/Unit/Node/Representation/Expr/PreIncTest.php new file mode 100644 index 0000000..adf8cfe --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/PreIncTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new PreInc($this->nodeRepresentationService, $node); + + $this->assertEquals('++$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/Print_Test.php b/tests/Unit/Node/Representation/Expr/Print_Test.php new file mode 100644 index 0000000..799daaa --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/Print_Test.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new Print_($this->nodeRepresentationService, $node); + + $this->assertEquals('print($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/PropertyFetchTest.php b/tests/Unit/Node/Representation/Expr/PropertyFetchTest.php new file mode 100644 index 0000000..741fbf9 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/PropertyFetchTest.php @@ -0,0 +1,42 @@ +createVariableNode('this'), + 'x', + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$this', 'x'); + + $representation = new PropertyFetch($this->nodeRepresentationService, $node); + + $this->assertEquals('$this->x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/ShellExecTest.php b/tests/Unit/Node/Representation/Expr/ShellExecTest.php new file mode 100644 index 0000000..45beebc --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/ShellExecTest.php @@ -0,0 +1,43 @@ +createVariableNode('command'), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$command']); + + $representation = new ShellExec($this->nodeRepresentationService, $node); + + $this->assertEquals('shell_exec($command)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/StaticCallTest.php b/tests/Unit/Node/Representation/Expr/StaticCallTest.php new file mode 100644 index 0000000..c8bb02e --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/StaticCallTest.php @@ -0,0 +1,48 @@ +createNameNode('Classname'), + 'x', + [ + $this->createArgNode($this->createVariableNode('x')), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('Classname', 'x'); + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new StaticCall($this->nodeRepresentationService, $node); + + $this->assertEquals('Classname::x($x)', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/StaticPropertyFetchTest.php b/tests/Unit/Node/Representation/Expr/StaticPropertyFetchTest.php new file mode 100644 index 0000000..6347ac6 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/StaticPropertyFetchTest.php @@ -0,0 +1,42 @@ +createNameNode('Classname'), + 'X', + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('Classname', 'X'); + + $representation = new StaticPropertyFetch($this->nodeRepresentationService, $node); + + $this->assertEquals('Classname::X', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/TernaryTest.php b/tests/Unit/Node/Representation/Expr/TernaryTest.php new file mode 100644 index 0000000..ba8c06f --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/TernaryTest.php @@ -0,0 +1,43 @@ +createVariableNode('x'), + $this->createVariableNode('a'), + $this->createVariableNode('b'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new Ternary($this->nodeRepresentationService, $node); + + $this->assertEquals('$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/UnaryMinusTest.php b/tests/Unit/Node/Representation/Expr/UnaryMinusTest.php new file mode 100644 index 0000000..3c92e89 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/UnaryMinusTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new UnaryMinus($this->nodeRepresentationService, $node); + + $this->assertEquals('-$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/UnaryPlusTest.php b/tests/Unit/Node/Representation/Expr/UnaryPlusTest.php new file mode 100644 index 0000000..4d6c412 --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/UnaryPlusTest.php @@ -0,0 +1,41 @@ +createVariableNode('x'), + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x'); + + $representation = new UnaryPlus($this->nodeRepresentationService, $node); + + $this->assertEquals('+$x', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Expr/VariableTest.php b/tests/Unit/Node/Representation/Expr/VariableTest.php new file mode 100644 index 0000000..9f7d3ea --- /dev/null +++ b/tests/Unit/Node/Representation/Expr/VariableTest.php @@ -0,0 +1,59 @@ +getNodeAttributes() + ); + + $representation = new Variable($this->nodeRepresentationService, $node); + + $this->assertEquals('$test', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithoutString(): void + { + $node = new Node\Expr\Variable( // $$test + new Node\Expr\Variable( + 'test', + $this->getNodeAttributes() + ), + $this->getNodeAttributes() + ); + + $representation = new Variable($this->nodeRepresentationService, $node); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$test'); + + $this->assertEquals('$$test', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/IdentifierTest.php b/tests/Unit/Node/Representation/IdentifierTest.php new file mode 100644 index 0000000..02275b3 --- /dev/null +++ b/tests/Unit/Node/Representation/IdentifierTest.php @@ -0,0 +1,36 @@ +getNodeAttributes() + ); + + $representation = new Identifier($this->nodeRepresentationService, $node); + + $this->assertEquals('test', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Name/FullyQualifiedTest.php b/tests/Unit/Node/Representation/Name/FullyQualifiedTest.php new file mode 100644 index 0000000..a005db9 --- /dev/null +++ b/tests/Unit/Node/Representation/Name/FullyQualifiedTest.php @@ -0,0 +1,63 @@ +getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('Exception'); + + $representation = new FullyQualified($this->nodeRepresentationService, $node); + + $this->assertEquals('\\Exception', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationWithNamespacedClassname(): void + { + $node = new Node\Name\FullyQualified( + [ + 'MyNamespace', + 'Exception', + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('MyNamespace', 'Exception'); + + $representation = new FullyQualified($this->nodeRepresentationService, $node); + + $this->assertEquals('\\MyNamespace\\Exception', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/NameTest.php b/tests/Unit/Node/Representation/NameTest.php new file mode 100644 index 0000000..4bd9243 --- /dev/null +++ b/tests/Unit/Node/Representation/NameTest.php @@ -0,0 +1,54 @@ +getNodeAttributes() + ); + + $representation = new Name($this->nodeRepresentationService, $node); + + $this->assertEquals('test', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationMultiple(): void + { + $node = new Node\Name( + [ + 'test', + 'test2', + ], + $this->getNodeAttributes() + ); + + $representation = new Name($this->nodeRepresentationService, $node); + + $this->assertEquals('test\\test2', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/ParamTest.php b/tests/Unit/Node/Representation/ParamTest.php new file mode 100644 index 0000000..dac19b5 --- /dev/null +++ b/tests/Unit/Node/Representation/ParamTest.php @@ -0,0 +1,76 @@ + ['$value', $this->createVariableNode('value'), null, null, false, false], + 'byRef' => ['&$value', $this->createVariableNode('value'), null, null, true, false], + 'returnType byRef' => ['int &$value', $this->createVariableNode('value'), $this->createIdentifierNode('int'), null, true, false], + 'returnType' => ['int $value', $this->createVariableNode('value'), $this->createIdentifierNode('int'), null, false, false], + 'default' => ['$value = $x', $this->createVariableNode('value'), null, $this->createVariableNode('x'), false, false], + 'default returnType' => ['int $value = $x', $this->createVariableNode('value'), $this->createIdentifierNode('int'), $this->createVariableNode('x'), false, false], + 'variadic' => ['...$value', $this->createVariableNode('value'), null, null, false, true], + 'variadic returnType' => ['int ...$value', $this->createVariableNode('value'), $this->createIdentifierNode('int'), null, false, true], + ]; + } + + /** + * @param string $expectedOutput + * @param Node\Expr\Variable $value + * @param Node\Identifier|null $type + * @param Node\Expr|null $default + * @param bool $byRef + * @param bool $variadic + * + * @return void + * + * @dataProvider argProvider + */ + public function testGetRepresentation(string $expectedOutput, Node\Expr\Variable $value, ?Node\Identifier $type, ?Node\Expr $default, bool $byRef, bool $variadic): void + { + $node = new Node\Param( + $value, + $default, + $type, + $byRef, + $variadic, + $this->getNodeAttributes() + ); + + if (null !== $default) { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$x', '$value'); + } else { + $this->nodeRepresentationService + ->method('representationForNode') + ->willReturn('$value'); + } + + $representation = new Param($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/DNumberTest.php b/tests/Unit/Node/Representation/Scalar/DNumberTest.php new file mode 100644 index 0000000..2b34406 --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/DNumberTest.php @@ -0,0 +1,37 @@ +getNodeAttributes() + ); + + $representation = new DNumber($this->nodeRepresentationService, $node); + + $this->assertEquals('1.337', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/EncapsedStringPartTest.php b/tests/Unit/Node/Representation/Scalar/EncapsedStringPartTest.php new file mode 100644 index 0000000..38a816a --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/EncapsedStringPartTest.php @@ -0,0 +1,37 @@ +getNodeAttributes() + ); + + $representation = new EncapsedStringPart($this->nodeRepresentationService, $node); + + $this->assertEquals('test', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/EncapsedTest.php b/tests/Unit/Node/Representation/Scalar/EncapsedTest.php new file mode 100644 index 0000000..c39ab94 --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/EncapsedTest.php @@ -0,0 +1,66 @@ +createVariableNode('x'), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['$x']); + + $representation = new Encapsed($this->nodeRepresentationService, $node); + + $this->assertEquals('"$x"', $representation->representation()); + } + + /** + * @return void + */ + public function testGetRepresentationMultiple(): void + { + $node = new Node\Scalar\Encapsed( + [ + new Node\Scalar\EncapsedStringPart('\\', $this->getNodeAttributes()), + $this->createVariableNode('x'), + ], + $this->getNodeAttributes() + ); + + $this->nodeRepresentationService + ->method('representationForArguments') + ->willReturn(['\\', '$x']); + + $representation = new Encapsed($this->nodeRepresentationService, $node); + + $this->assertEquals('"\\$x"', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/LNumberTest.php b/tests/Unit/Node/Representation/Scalar/LNumberTest.php new file mode 100644 index 0000000..2b6c920 --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/LNumberTest.php @@ -0,0 +1,37 @@ +getNodeAttributes() + ); + + $representation = new LNumber($this->nodeRepresentationService, $node); + + $this->assertEquals('1337', $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/MagicConstTest.php b/tests/Unit/Node/Representation/Scalar/MagicConstTest.php new file mode 100644 index 0000000..83e217a --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/MagicConstTest.php @@ -0,0 +1,58 @@ + ['__CLASS__', Node\Scalar\MagicConst\Class_::class], + 'dir' => ['__DIR__', Node\Scalar\MagicConst\Dir::class], + 'file' => ['__FILE__', Node\Scalar\MagicConst\File::class], + 'function' => ['__FUNCTION__', Node\Scalar\MagicConst\Function_::class], + 'line' => ['__LINE__', Node\Scalar\MagicConst\Line::class], + 'method' => ['__METHOD__', Node\Scalar\MagicConst\Method::class], + 'namespace' => ['__NAMESPACE__', Node\Scalar\MagicConst\Namespace_::class], + 'trait' => ['__TRAIT__', Node\Scalar\MagicConst\Trait_::class], + ]; + } + + /** + * @param string $expectedOutput + * @param string $nodeClassname + * + * @return void + * + * @dataProvider magicConstProvider + */ + public function testGetRepresentation(string $expectedOutput, string $nodeClassname): void + { + $node = new $nodeClassname( + $this->getNodeAttributes() + ); + + $representation = new MagicConst($this->nodeRepresentationService, $node); + + $this->assertEquals($expectedOutput, $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/Scalar/String_Test.php b/tests/Unit/Node/Representation/Scalar/String_Test.php new file mode 100644 index 0000000..edbe653 --- /dev/null +++ b/tests/Unit/Node/Representation/Scalar/String_Test.php @@ -0,0 +1,37 @@ +getNodeAttributes() + ); + + $representation = new String_($this->nodeRepresentationService, $node); + + $this->assertEquals("'test'", $representation->representation()); + } +} diff --git a/tests/Unit/Node/Representation/VarLikeIdentifierTest.php b/tests/Unit/Node/Representation/VarLikeIdentifierTest.php new file mode 100644 index 0000000..8726869 --- /dev/null +++ b/tests/Unit/Node/Representation/VarLikeIdentifierTest.php @@ -0,0 +1,36 @@ +getNodeAttributes() + ); + + $representation = new VarLikeIdentifier($this->nodeRepresentationService, $node); + + $this->assertEquals('$test', $representation->representation()); + } +} diff --git a/tests/Unit/Node/TraverserTest.php b/tests/Unit/Node/TraverserTest.php new file mode 100644 index 0000000..6ff575b --- /dev/null +++ b/tests/Unit/Node/TraverserTest.php @@ -0,0 +1,74 @@ +traverser = new Traverser(); + } + + /** + * @return void + */ + public function testUpdateFileInVisitor(): void + { + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + /** @var MockObject|VisitorInterface $visitor */ + $visitor = $this->createMock(AbstractVisitor::class); + $visitor + ->expects($this->once()) + ->method('setFile') + ->with($file); + + $this->traverser->addVisitor($visitor); + $this->traverser->setFile($file); + } + + /** + * @return void + */ + public function testGetNodeOccurrencesCount(): void + { + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $node = $this->createVariableNode(); + $nodeOccurrenceList = new OccurrenceList(); + $nodeOccurrenceList->addOccurrence(new Occurrence($node, $file)); + $nodeOccurrenceList->addOccurrence(new Occurrence($node, $file)); + $nodeOccurrenceList->addOccurrence(new Occurrence($node, $file)); + /** @var MockObject|VisitorInterface $visitor */ + $visitor = $this->createMock(AbstractVisitor::class); + $visitor + ->method('getNodeOccurrenceList') + ->willReturn($nodeOccurrenceList); + + $this->traverser->addVisitor($visitor); + + $this->assertEquals(3, $this->traverser->getNodeOccurrencesCount()); + } +} diff --git a/tests/Unit/Node/Visitor/BooleanReturnConditionVisitorTest.php b/tests/Unit/Node/Visitor/BooleanReturnConditionVisitorTest.php new file mode 100644 index 0000000..b1a019e --- /dev/null +++ b/tests/Unit/Node/Visitor/BooleanReturnConditionVisitorTest.php @@ -0,0 +1,105 @@ +visitor = new BooleanReturnConditionVisitor(); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $this->visitor->setFile($file); + } + + /** + * @return void + */ + public function testEnterNode(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add :bool functions, no coalesce + $node = new Coalesce($this->createVariableNode('x'), $this->createVariableNode('y')); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + $expr = new Identical( + $this->createVariableNode('x'), + $this->createScalarStringNode('foo'), + $this->getNodeAttributes() + ); + + $stmts = [ + new Return_( + $expr, + $this->getNodeAttributes() + ), + ]; + + // function without return type declaration (:bool) + $node = new Function_( + $this->createIdentifierNode('test'), + [ + 'stmts' => $stmts, + 'returnType' => null, + ], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // function with wrong return type declaration (here string) + $node = new Function_( + $this->createIdentifierNode('test'), + [ + 'stmts' => $stmts, + 'returnType' => $this->createIdentifierNode('string'), + ], + $this->getNodeAttributes() + ); + + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added + $node = new Function_( + $this->createIdentifierNode('test'), + [ + 'stmts' => $stmts, + 'returnType' => $this->createIdentifierNode('bool'), + ], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(1, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $this->visitor->getNodeOccurrenceList()->getOccurrences()->first(); + $this->assertSame($expr, $occurrence->getNode()); + } +} diff --git a/tests/Unit/Node/Visitor/CoalesceConditionVisitorTest.php b/tests/Unit/Node/Visitor/CoalesceConditionVisitorTest.php new file mode 100644 index 0000000..ddafe75 --- /dev/null +++ b/tests/Unit/Node/Visitor/CoalesceConditionVisitorTest.php @@ -0,0 +1,58 @@ +visitor = new CoalesceConditionVisitor(); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $this->visitor->setFile($file); + } + + /** + * @return void + */ + public function testEnterNode(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add coalesce, not If_ + $node = new If_($this->createScalarStringNode('xxx'), [], $this->getNodeAttributes()); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added + $node = new Coalesce($this->createVariableNode('x'), $this->createVariableNode('y')); + $this->visitor->enterNode($node); + $this->assertCount(1, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $this->visitor->getNodeOccurrenceList()->getOccurrences()->first(); + $this->assertSame($node->left, $occurrence->getNode()); + } +} diff --git a/tests/Unit/Node/Visitor/ElseIfConditionVisitorTest.php b/tests/Unit/Node/Visitor/ElseIfConditionVisitorTest.php new file mode 100644 index 0000000..1969f7c --- /dev/null +++ b/tests/Unit/Node/Visitor/ElseIfConditionVisitorTest.php @@ -0,0 +1,97 @@ +visitor = new ElseIfConditionVisitor(); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $this->visitor->setFile($file); + } + + /** + * @return void + */ + public function testEnterNode(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should inspect If_ conditions and edd elseif conditions, not Ternary + $node = new Ternary($this->createVariableNode('x'), $this->createVariableNode('y'), $this->createVariableNode('z')); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should not be added (because it's not in elseif) + $node = new If_( + $this->createScalarStringNode('xxx'), + [], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + } + + /** + * @return void + */ + public function testEnterNodeElseIfs(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add If_ conditions, not Ternary + $node = new Ternary($this->createVariableNode('x'), $this->createVariableNode('y'), $this->createVariableNode('z')); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added (and count as 2 because the '0' is the if and there are just 2 elseif's) + $node = new If_( + $this->createScalarStringNode('0'), + [ + 'elseifs' => [ + new ElseIf_($this->createScalarStringNode('1'), $this->getNodeAttributes()), + new ElseIf_($this->createScalarStringNode('2'), $this->getNodeAttributes()), + ], + ], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(2, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** + * @var int $key + * @var Occurrence $occurrence + */ + foreach ($this->visitor->getNodeOccurrenceList()->getOccurrences() as $key => $occurrence) { + /** @var String_ $node */ + $node = $occurrence->getNode(); + $this->assertEquals($key + 1, $node->value); + } + } +} diff --git a/tests/Unit/Node/Visitor/IfConditionVisitorTest.php b/tests/Unit/Node/Visitor/IfConditionVisitorTest.php new file mode 100644 index 0000000..ba27ca8 --- /dev/null +++ b/tests/Unit/Node/Visitor/IfConditionVisitorTest.php @@ -0,0 +1,101 @@ +visitor = new IfConditionVisitor(); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $this->visitor->setFile($file); + } + + /** + * @return void + */ + public function testEnterNode(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add If_ conditions, not Ternary + $node = new Ternary($this->createVariableNode('x'), $this->createVariableNode('y'), $this->createVariableNode('z')); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added (and count as 1) + $node = new If_( + $this->createScalarStringNode('xxx'), + [], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(1, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $this->visitor->getNodeOccurrenceList()->getOccurrences()->first(); + $this->assertSame($node->cond, $occurrence->getNode()); + } + + /** + * @return void + */ + public function testEnterNodeElseIfs(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add If_ conditions, not Ternary + $node = new Ternary($this->createVariableNode('x'), $this->createVariableNode('y'), $this->createVariableNode('z')); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added (and count as 1 because elseifs should not get added) + $node = new If_( + $this->createScalarStringNode('0'), + [ + 'elseifs' => [ + new ElseIf_($this->createScalarStringNode('1'), $this->getNodeAttributes()), + new ElseIf_($this->createScalarStringNode('2'), $this->getNodeAttributes()), + ], + ], + $this->getNodeAttributes() + ); + $this->visitor->enterNode($node); + $this->assertCount(1, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** + * @var int $key + * @var Occurrence $occurrence + */ + foreach ($this->visitor->getNodeOccurrenceList()->getOccurrences() as $key => $occurrence) { + /** @var String_ $node */ + $node = $occurrence->getNode(); + $this->assertEquals($key, $node->value); + } + } +} diff --git a/tests/Unit/Node/Visitor/TernaryConditionVisitorTest.php b/tests/Unit/Node/Visitor/TernaryConditionVisitorTest.php new file mode 100644 index 0000000..1d5d307 --- /dev/null +++ b/tests/Unit/Node/Visitor/TernaryConditionVisitorTest.php @@ -0,0 +1,58 @@ +visitor = new TernaryConditionVisitor(); + + /** @var MockObject|SplFileInfo $file */ + $file = $this->createSplFileInfoMock(); + + $this->visitor->setFile($file); + } + + /** + * @return void + */ + public function testEnterNode(): void + { + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // visitor just should add ternary conditions, not If_ + $node = new If_($this->createScalarStringNode('xxx'), [], $this->getNodeAttributes()); + $this->visitor->enterNode($node); + $this->assertCount(0, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + // this should be added + $node = new Ternary($this->createVariableNode('x'), $this->createVariableNode('y'), $this->createVariableNode('z')); + $this->visitor->enterNode($node); + $this->assertCount(1, $this->visitor->getNodeOccurrenceList()->getOccurrences()); + + /** @var Occurrence $occurrence */ + $occurrence = $this->visitor->getNodeOccurrenceList()->getOccurrences()->first(); + $this->assertSame($node->cond, $occurrence->getNode()); + } +} diff --git a/tests/Unit/Service/NodeRepresentationServiceTest.php b/tests/Unit/Service/NodeRepresentationServiceTest.php new file mode 100644 index 0000000..b1c1023 --- /dev/null +++ b/tests/Unit/Service/NodeRepresentationServiceTest.php @@ -0,0 +1,102 @@ +nodeRepresentationService = new NodeRepresentationService(); + } + + /** + * @return void + */ + public function testGetRepresentationForNode(): void + { + $node = $this->createNameNode('test'); + + $this->assertEquals('test', $this->nodeRepresentationService->representationForNode($node)); + } + + /** + * @return void + */ + public function testGetAbstractRepresentationForNode(): void + { + $node = new Int_($this->createVariableNode('i'), $this->getNodeAttributes()); + + $this->assertEquals('(int) $i', $this->nodeRepresentationService->representationForNode($node)); + } + + /** + * @return void + */ + public function testGetRepresentationForNodeWillThrowExceptionWhenThereIsNoRepresentationClass(): void + { + $node = new Stmt\Trait_($this->createIdentifierNode('test')); + + $this->expectException(NodeRepresentationClassDoesNotExistException::class); + $this->expectExceptionMessage('No Representation for node PhpParser\Node\Stmt\Trait_ found'); + + $this->nodeRepresentationService->representationForNode($node); + } + + /** + * @return void + */ + public function testGetArgumentsEmpty(): void + { + $this->assertCount(0, $this->nodeRepresentationService->representationForArguments([])); + } + + /** + * @return void + */ + public function testGetArgumentsSingleArgument(): void + { + $arguments = [ + $this->createVariableNode('test'), + ]; + + $transformedArguments = $this->nodeRepresentationService->representationForArguments($arguments); + + $this->assertCount(1, $transformedArguments); + $this->assertEquals('$test', $transformedArguments[0]); + } + + /** + * @return void + */ + public function testGetArgumentsMultipleArguments(): void + { + $arguments = [ + $this->createVariableNode('test'), + $this->createVariableNode('test2'), + ]; + + $transformedArguments = $this->nodeRepresentationService->representationForArguments($arguments); + + $this->assertCount(2, $transformedArguments); + $this->assertEquals('$test', $transformedArguments[0]); + $this->assertEquals('$test2', $transformedArguments[1]); + } +} \ No newline at end of file diff --git a/tests/Unit/Service/SortServiceTest.php b/tests/Unit/Service/SortServiceTest.php new file mode 100644 index 0000000..c57e0b0 --- /dev/null +++ b/tests/Unit/Service/SortServiceTest.php @@ -0,0 +1,170 @@ +sortService = new SortService(); + } + + /** + * @return void + */ + public function testSortArrayCollection(): void + { + $collection = new ArrayCollection([ + 0 => [ + 'count' => 5, + ], + 1 => [ + 'count' => 77, + ], + 2 => [ + 'count' => 3, + ], + ]); + + $sortConfiguration = new Sort(new ArrayCollection([ + new SortField('count', 'DESC'), + ])); + + $sortedCollection = $this->sortService->sortArrayCollection($collection, $sortConfiguration); + $this->assertEquals([ + 1 => [ + 'count' => 77, + ], + 0 => [ + 'count' => 5, + ], + 2 => [ + 'count' => 3, + ], + ], $sortedCollection->toArray()); + } + + /** + * @return void + */ + public function testSortArrayCollectionWithMultipleFields(): void + { + $collection = new ArrayCollection([ + 0 => [ + 'count' => 100, + 'name' => 'foo', + ], + 1 => [ + 'count' => 100, + 'name' => 'bar', + ], + 2 => [ + 'count' => 100, + 'name' => 'apple', + ], + ]); + + $sortConfiguration = new Sort(new ArrayCollection([ + new SortField('count', 'DESC'), + new SortField('name', 'ASC'), + ])); + + $sortedCollection = $this->sortService->sortArrayCollection($collection, $sortConfiguration); + $this->assertEquals([ + 2 => [ + 'count' => 100, + 'name' => 'apple', + ], + 1 => [ + 'count' => 100, + 'name' => 'bar', + ], + 0 => [ + 'count' => 100, + 'name' => 'foo', + ], + ], $sortedCollection->toArray()); + } + + /** + * @return void + */ + public function testSortArrayCollectionWithFirstResult(): void + { + $collection = new ArrayCollection([ + 0 => [ + 'count' => 5, + ], + 1 => [ + 'count' => 77, + ], + 2 => [ + 'count' => 3, + ], + ]); + + $sortConfiguration = new Sort(new ArrayCollection([ + new SortField('count', 'DESC'), + ]), 1); + + $sortedCollection = $this->sortService->sortArrayCollection($collection, $sortConfiguration); + $this->assertEquals([ + 0 => [ + 'count' => 5, + ], + 1 => [ + 'count' => 3, + ], + ], $sortedCollection->toArray()); + } + + /** + * @return void + */ + public function testSortArrayCollectionWithMaximumResults(): void + { + $collection = new ArrayCollection([ + 0 => [ + 'count' => 5, + ], + 1 => [ + 'count' => 77, + ], + 2 => [ + 'count' => 3, + ], + ]); + + $sortConfiguration = new Sort(new ArrayCollection([ + new SortField('count', 'DESC'), + ]), null, 2); + + $sortedCollection = $this->sortService->sortArrayCollection($collection, $sortConfiguration); + $this->assertEquals([ + 0 => [ + 'count' => 77, + ], + 1 => [ + 'count' => 5, + ], + ], $sortedCollection->toArray()); + } +} \ No newline at end of file diff --git a/tests/data/most_used_conditions_integrationtest.php b/tests/data/most_used_conditions_integrationtest.php new file mode 100644 index 0000000..19b50d5 --- /dev/null +++ b/tests/data/most_used_conditions_integrationtest.php @@ -0,0 +1,29 @@ +getUser()) {} + +if ((bool) $someVar) {} elseif ((int) $someVar !== 0) + +if (!($i < 3)) {} + +if (!($i <= 13)) {} + +if (!(30 === date('d') && !(null === $user))) {} + +if (\strtolower('Chris') === 'chris') {} + +$page = isset($_GET['page']) ? $_GET['page'] : 1; + +$page = $_GET['page'] ?? 1; + +function abc($x, $y): bool +{ + return $x === + $y; +} \ No newline at end of file