diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index a29ac18..c2fd5ee 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -3,6 +3,8 @@ on: push jobs: phpunit: runs-on: ubuntu-latest + env: + IS_CI_TEST: true steps: - uses: actions/checkout@v1 - uses: shivammathur/setup-php@v2 diff --git a/.gitignore b/.gitignore index 4da6094..99cb968 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ vendor var +!var/tests/repo !var/**/.gitkeep .phpunit.result.cache diff --git a/README.md b/README.md new file mode 100644 index 0000000..95cd4ff --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# PHPCS Documentation Generator + +Note: this currently works only with the PHPCompatibility standard. + +## Usage + +Copy the `generator.xml.dist` template to `generator.xml` and add your sources and standards. + +```sh +bin/phpcsdocs generate +``` diff --git a/composer.json b/composer.json index 1242441..c381f2c 100644 --- a/composer.json +++ b/composer.json @@ -6,9 +6,12 @@ "danielstjules/stringy": "^3.1", "roave/better-reflection": "^4.12", "phpdocumentor/reflection-docblock": "^5.2", - "ext-simplexml": "*", "symfony/filesystem": "^5.2", - "symfony/process": "^5.2" + "symfony/process": "^5.2", + "nikic/php-parser": "4.6.*", + "ext-simplexml": "*", + "ext-dom": "*", + "ext-libxml": "*" }, "autoload": { "psr-4": { @@ -17,6 +20,6 @@ } }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.0" } } diff --git a/composer.lock b/composer.lock index 89eee70..fad8cb9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d1e357dd79e21303096f74a1a97631ed", + "content-hash": "27502a4c19ce2892bc1f63c5028e2b76", "packages": [ { "name": "beberlei/assert", @@ -180,16 +180,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.10.4", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + "reference": "c346bbfafe2ff60680258b631afb730d186ed864" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c346bbfafe2ff60680258b631afb730d186ed864", + "reference": "c346bbfafe2ff60680258b631afb730d186ed864", "shasum": "" }, "require": { @@ -197,8 +197,8 @@ "php": ">=7.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "ircmaxell/php-yacc": "0.0.5", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" }, "bin": [ "bin/php-parse" @@ -206,7 +206,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -230,9 +230,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.6.0" }, - "time": "2020-12-20T10:01:03+00:00" + "time": "2020-07-02T17:12:47+00:00" }, { "name": "opis/closure", @@ -763,26 +763,26 @@ }, { "name": "roave/signature", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/Roave/Signature.git", - "reference": "bbbcc317dfe3a750e27231c5d2130153aa4c41b3" + "reference": "5b5bb9499cfbcc78d9f472e03af1e97e4341ec7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/Signature/zipball/bbbcc317dfe3a750e27231c5d2130153aa4c41b3", - "reference": "bbbcc317dfe3a750e27231c5d2130153aa4c41b3", + "url": "https://api.github.com/repos/Roave/Signature/zipball/5b5bb9499cfbcc78d9f472e03af1e97e4341ec7c", + "reference": "5b5bb9499cfbcc78d9f472e03af1e97e4341ec7c", "shasum": "" }, "require": { - "php": "7.4.*" + "php": "7.4.*|8.0.*" }, "require-dev": { - "doctrine/coding-standard": "^8.1", - "infection/infection": "^0.17.5", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "^3.16" + "doctrine/coding-standard": "^8.2", + "infection/infection": "^0.20.2", + "phpunit/phpunit": "^9.5.1", + "vimeo/psalm": "^4.4" }, "type": "library", "autoload": { @@ -797,22 +797,22 @@ "description": "Sign and verify stuff", "support": { "issues": "https://github.com/Roave/Signature/issues", - "source": "https://github.com/Roave/Signature/tree/1.3.0" + "source": "https://github.com/Roave/Signature/tree/1.4.0" }, - "time": "2020-10-01T08:35:57+00:00" + "time": "2021-01-25T09:39:37+00:00" }, { "name": "symfony/console", - "version": "v5.2.1", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "47c02526c532fb381374dab26df05e7313978976" + "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/47c02526c532fb381374dab26df05e7313978976", - "reference": "47c02526c532fb381374dab26df05e7313978976", + "url": "https://api.github.com/repos/symfony/console/zipball/d62ec79478b55036f65e2602e282822b8eaaff0a", + "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a", "shasum": "" }, "require": { @@ -871,7 +871,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ "cli", @@ -880,7 +880,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.1" + "source": "https://github.com/symfony/console/tree/v5.2.2" }, "funding": [ { @@ -896,20 +896,20 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/filesystem", - "version": "v5.2.1", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d" + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fa8f8cab6b65e2d99a118e082935344c5ba8c60d", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038", + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038", "shasum": "" }, "require": { @@ -939,10 +939,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.2.1" + "source": "https://github.com/symfony/filesystem/tree/v5.2.2" }, "funding": [ { @@ -958,7 +958,7 @@ "type": "tidelift" } ], - "time": "2020-11-30T17:05:38+00:00" + "time": "2021-01-27T10:01:46+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1448,16 +1448,16 @@ }, { "name": "symfony/process", - "version": "v5.2.1", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd" + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bd8815b8b6705298beaa384f04fabd459c10bedd", - "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd", + "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "shasum": "" }, "require": { @@ -1487,10 +1487,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.2.1" + "source": "https://github.com/symfony/process/tree/v5.2.2" }, "funding": [ { @@ -1506,7 +1506,7 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:03:37+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/service-contracts", @@ -1589,16 +1589,16 @@ }, { "name": "symfony/string", - "version": "v5.2.1", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed" + "reference": "c95468897f408dd0aca2ff582074423dd0455122" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", + "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", + "reference": "c95468897f408dd0aca2ff582074423dd0455122", "shasum": "" }, "require": { @@ -1641,7 +1641,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -1652,7 +1652,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.1" + "source": "https://github.com/symfony/string/tree/v5.2.2" }, "funding": [ { @@ -1668,7 +1668,7 @@ "type": "tidelift" } ], - "time": "2020-12-05T07:33:16+00:00" + "time": "2021-01-25T15:14:59+00:00" }, { "name": "webmozart/assert", @@ -1854,29 +1854,28 @@ }, { "name": "phar-io/manifest", - "version": "2.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -1910,24 +1909,24 @@ "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/master" }, - "time": "2020-06-27T14:33:11+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "3.0.4", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^5.6 || ^7.0" }, "type": "library", "autoload": { @@ -1959,9 +1958,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.0.4" + "source": "https://github.com/phar-io/version/tree/master" }, - "time": "2020-12-13T23:18:30+00:00" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpspec/prophecy", @@ -2032,35 +2031,32 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.5", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1" + "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1", - "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca6647ffddd2add025ab3f21644a441d7c146cdc", + "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc", "shasum": "" }, "require": { "ext-dom": "*", - "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "php": "^7.3", + "phpunit/php-file-iterator": "^3.0", + "phpunit/php-text-template": "^2.0", + "phpunit/php-token-stream": "^4.0", + "sebastian/code-unit-reverse-lookup": "^2.0", + "sebastian/environment": "^5.0", + "sebastian/version": "^3.0", + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "suggest": { "ext-pcov": "*", @@ -2069,7 +2065,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-master": "8.0-dev" } }, "autoload": { @@ -2097,7 +2093,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/8.0.2" }, "funding": [ { @@ -2105,7 +2101,7 @@ "type": "github" } ], - "time": "2020-11-28T06:44:49+00:00" + "time": "2020-05-23T08:02:54+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2349,72 +2345,35 @@ "time": "2020-10-26T13:16:10+00:00" }, { - "name": "phpunit/phpunit", - "version": "9.5.1", + "name": "phpunit/php-token-stream", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360" + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7bdf4085de85a825f4424eae52c99a1cec2f360", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.1", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.3", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3", - "sebastian/version": "^3.0.2" + "ext-tokenizer": "*", + "php": "^7.3 || ^8.0" }, "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "phpunit/phpunit": "^9.0" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" - ], - "files": [ - "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2424,62 +2383,93 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ - "phpunit", - "testing", - "xunit" + "tokenizer" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.1" + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" }, "funding": [ - { - "url": "https://phpunit.de/donate.html", - "type": "custom" - }, { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2021-01-17T07:42:25+00:00" + "abandoned": true, + "time": "2020-08-04T08:28:15+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "phpunit/phpunit", + "version": "9.2.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "1c6a9e4312e209e659f1fce3ce88dd197c2448f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c6a9e4312e209e659f1fce3ce88dd197c2448f6", + "reference": "1c6a9e4312e209e659f1fce3ce88dd197c2448f6", "shasum": "" }, "require": { - "php": ">=7.3" + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.9.5", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", + "php": "^7.3", + "phpspec/prophecy": "^1.10.3", + "phpunit/php-code-coverage": "^8.0.2", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-invoker": "^3.0.2", + "phpunit/php-text-template": "^2.0.2", + "phpunit/php-timer": "^5.0.1", + "sebastian/code-unit": "^1.0.5", + "sebastian/comparator": "^4.0.3", + "sebastian/diff": "^4.0.1", + "sebastian/environment": "^5.1.2", + "sebastian/exporter": "^4.0.2", + "sebastian/global-state": "^4.0", + "sebastian/object-enumerator": "^4.0.2", + "sebastian/resource-operations": "^3.0.2", + "sebastian/type": "^2.1.1", + "sebastian/version": "^3.0.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "9.2-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2493,19 +2483,28 @@ "role": "lead" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.2.6" }, "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2020-07-13T17:55:55+00:00" }, { "name": "sebastian/code-unit", @@ -2692,63 +2691,6 @@ ], "time": "2020-10-26T15:49:45+00:00" }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, { "name": "sebastian/diff", "version": "4.0.4", @@ -2957,26 +2899,26 @@ }, { "name": "sebastian/global-state", - "version": "5.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" + "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bdb1e7c79e592b8c82cb1699be3c8743119b8a72", + "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72", "shasum": "" }, "require": { - "php": ">=7.3", + "php": "^7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "suggest": { "ext-uopz": "*" @@ -2984,7 +2926,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -3009,72 +2951,9 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:55:19+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } + "source": "https://github.com/sebastianbergmann/global-state/tree/master" }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2020-02-07T06:11:37+00:00" }, { "name": "sebastian/object-enumerator", @@ -3472,7 +3351,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "ext-simplexml": "*" + "ext-simplexml": "*", + "ext-dom": "*", + "ext-libxml": "*" }, "platform-dev": [], "plugin-api-version": "2.0.0" diff --git a/config/di.php b/config/di.php index 85b2b4e..b51af2d 100644 --- a/config/di.php +++ b/config/di.php @@ -1,15 +1,18 @@ <?php declare(strict_types=1); -use App\CodeRepository\CodeRepository; -use App\CodeRepository\GithubCodeRepository; +use App\Configuration\ConfigurationRepository; +use App\Configuration\XmlConfigurationRepository; use App\Generator\Generator as DocGenerator; use App\Generator\MarkdownGenerator; use App\SniffFinder\FilesystemSniffFinder; use App\SniffFinder\SniffFinder; +use App\Value\Folder; return [ - CodeRepository::class => DI\autowire(GithubCodeRepository::class), DocGenerator::class => DI\autowire(MarkdownGenerator::class), SniffFinder::class => DI\autowire(FilesystemSniffFinder::class), + ConfigurationRepository::class => DI\factory(function () { + return new XmlConfigurationRepository(new Folder(__DIR__ . '/../')); + }) ]; diff --git a/generator.xml.dist b/generator.xml.dist new file mode 100644 index 0000000..647bea3 --- /dev/null +++ b/generator.xml.dist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<generator format="markdown"> + <source path="git@github.com:PHPCompatibility/PHPCompatibility.git"> + <standard path="PHPCompatibility" /> + </source> +</generator> diff --git a/src/CodeRepository/CodeRepository.php b/src/CodeRepository/CodeRepository.php index d132f32..1ff2784 100644 --- a/src/CodeRepository/CodeRepository.php +++ b/src/CodeRepository/CodeRepository.php @@ -3,11 +3,12 @@ namespace App\CodeRepository; +use App\Configuration\Value\Source; use App\Value\Folder; interface CodeRepository { public const CODE_DOWNLOAD_PATH = 'var/repos/'; - public function downloadCode(string $repoName): Folder; + public function getFolder(Source $source): Folder; } diff --git a/src/CodeRepository/CodeRepositoryFactory.php b/src/CodeRepository/CodeRepositoryFactory.php new file mode 100644 index 0000000..26f6ab9 --- /dev/null +++ b/src/CodeRepository/CodeRepositoryFactory.php @@ -0,0 +1,22 @@ +<?php +declare(strict_types=1); + +namespace App\CodeRepository; + +use App\Configuration\Value\Source; +use InvalidArgumentException; + +class CodeRepositoryFactory +{ + public function fromType(string $type): CodeRepository + { + switch ($type) { + case Source::TYPE_GIT: + return new GitCodeRepository(); + case Source::TYPE_LOCAL: + return new LocalCodeRepository(); + default: + throw new InvalidArgumentException('Invalid type: ' . $type); + } + } +} diff --git a/src/CodeRepository/GithubCodeRepository.php b/src/CodeRepository/GitCodeRepository.php similarity index 60% rename from src/CodeRepository/GithubCodeRepository.php rename to src/CodeRepository/GitCodeRepository.php index 3f16837..4cb4dbf 100644 --- a/src/CodeRepository/GithubCodeRepository.php +++ b/src/CodeRepository/GitCodeRepository.php @@ -3,20 +3,19 @@ namespace App\CodeRepository; +use App\Configuration\Value\Source; use App\Value\Folder; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; -class GithubCodeRepository implements CodeRepository +class GitCodeRepository implements CodeRepository { - public function downloadCode(string $repoName): Folder + public function getFolder(Source $source): Folder { - $repoPath = new Folder(self::CODE_DOWNLOAD_PATH . $repoName . '/'); + $this->runProcess($this->getCloneOrPullProcess($source->getLocalFolder(), $source->getPath())); + $this->runProcess($this->getComposerInstallProcess($source->getLocalFolder())); - $this->runProcess($this->getCloneOrPullProcess($repoPath, $repoName)); - $this->runProcess($this->getComposerInstallProcess($repoPath)); - - return $repoPath; + return $source->getLocalFolder(); } private function runProcess(Process $process): void @@ -28,21 +27,21 @@ private function runProcess(Process $process): void } } - private function getCloneOrPullProcess(Folder $repoPath, string $repoName): Process + private function getCloneOrPullProcess(Folder $localPath, string $sourcePath): Process { - if (!is_dir((string)$repoPath)) { + if (!is_dir((string)$localPath)) { return new Process([ 'git', 'clone', - 'git@github.com:' . $repoName, - $repoPath + $sourcePath, + (string)$localPath ]); } return new Process([ 'git', '-C', - $repoPath, + (string)$localPath, 'pull' ]); } diff --git a/src/CodeRepository/LocalCodeRepository.php b/src/CodeRepository/LocalCodeRepository.php new file mode 100644 index 0000000..62594fa --- /dev/null +++ b/src/CodeRepository/LocalCodeRepository.php @@ -0,0 +1,38 @@ +<?php +declare(strict_types=1); + +namespace App\CodeRepository; + +use App\Configuration\Value\Source; +use App\Value\Folder; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; + +class LocalCodeRepository implements CodeRepository +{ + public function getFolder(Source $source): Folder + { + $this->runProcess($this->getComposerInstallProcess($source->getLocalFolder())); + + return $source->getLocalFolder(); + } + + private function runProcess(Process $process): void + { + $process->run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + } + + private function getComposerInstallProcess(Folder $repoPath): Process + { + return new Process([ + 'composer', + 'install', + '-d', + $repoPath + ]); + } +} diff --git a/src/Configuration/ConfigurationRepository.php b/src/Configuration/ConfigurationRepository.php new file mode 100644 index 0000000..959ff32 --- /dev/null +++ b/src/Configuration/ConfigurationRepository.php @@ -0,0 +1,11 @@ +<?php +declare(strict_types=1); + +namespace App\Configuration; + +use App\Configuration\Value\Configuration; + +interface ConfigurationRepository +{ + public function getConfig(): Configuration; +} diff --git a/src/Configuration/Value/Configuration.php b/src/Configuration/Value/Configuration.php new file mode 100644 index 0000000..bddd2aa --- /dev/null +++ b/src/Configuration/Value/Configuration.php @@ -0,0 +1,43 @@ +<?php +declare(strict_types=1); + +namespace App\Configuration\Value; + +use Assert\Assert; + +final class Configuration +{ + private string $format; + /** + * @var Source[] + */ + private array $sources; + + /** + * @param Source[] $sources + */ + public function __construct(string $format, array $sources) + { + Assert::that($format) + ->inArray(['markdown'], sprintf('Invalid generator format "%s"', $format)); + + Assert::thatAll($sources) + ->isInstanceOf(Source::class); + + $this->format = $format; + $this->sources = $sources; + } + + public function getFormat(): string + { + return $this->format; + } + + /** + * @return Source[] + */ + public function getSources(): array + { + return $this->sources; + } +} diff --git a/src/Configuration/Value/Source.php b/src/Configuration/Value/Source.php new file mode 100644 index 0000000..c7fa46b --- /dev/null +++ b/src/Configuration/Value/Source.php @@ -0,0 +1,68 @@ +<?php +declare(strict_types=1); + +namespace App\Configuration\Value; + +use App\CodeRepository\CodeRepository; +use App\Value\Folder; +use function Stringy\create as s; + +final class Source +{ + public const TYPE_GIT = 'git'; + public const TYPE_LOCAL = 'local'; + + private string $path; + /** + * @var Standard[] + */ + private array $standards; + private Folder $localFolder; + private string $type = self::TYPE_LOCAL; + + /** + * @param Standard[] $standards + */ + public function __construct(string $path, array $standards) + { + $this->path = $path; + $this->standards = $standards; + + if (preg_match('/([^.\/]+)\.git$/', $path, $matches)) { + $this->localFolder = $this->createLocalFolder(CodeRepository::CODE_DOWNLOAD_PATH . $matches[1]); + $this->type = self::TYPE_GIT; + return; + } + + $this->localFolder = $this->createLocalFolder($path); + } + + private function createLocalFolder(string $path): Folder + { + $path = s($path)->ensureRight('/'); + return new Folder((string)$path); + } + + public function getPath(): string + { + return $this->path; + } + + public function getLocalFolder(): Folder + { + return $this->localFolder; + } + + /** + * @return Standard[] + */ + public function getStandards(): array + { + return $this->standards; + } + + public function getType(): string + { + return $this->type; + } +} diff --git a/src/Configuration/Value/Standard.php b/src/Configuration/Value/Standard.php new file mode 100644 index 0000000..af4c35d --- /dev/null +++ b/src/Configuration/Value/Standard.php @@ -0,0 +1,24 @@ +<?php +declare(strict_types=1); + +namespace App\Configuration\Value; + +use Assert\Assert; + +final class Standard +{ + private string $path; + + public function __construct(string $path) + { + Assert::that($path) + ->notBlank(); + + $this->path = $path; + } + + public function getPath(): string + { + return $this->path; + } +} diff --git a/src/Configuration/XmlConfigurationRepository.php b/src/Configuration/XmlConfigurationRepository.php new file mode 100644 index 0000000..7b52817 --- /dev/null +++ b/src/Configuration/XmlConfigurationRepository.php @@ -0,0 +1,82 @@ +<?php +declare(strict_types=1); + +namespace App\Configuration; + +use App\Configuration\Value\Configuration; +use App\Configuration\Value\Source; +use App\Configuration\Value\Standard; +use App\Value\Folder; +use DOMDocument; +use RuntimeException; +use SimpleXMLElement; + +class XmlConfigurationRepository implements ConfigurationRepository +{ + private Folder $root; + + public function __construct(Folder $root) + { + $this->root = $root; + } + + public function getConfig(): Configuration + { + $paths = [$this->root . 'generator.xml', $this->root . 'generator.xml.dist']; + foreach ($paths as $path) { + if (file_exists($path)) { + return $this->parse($path); + } + } + + throw new RuntimeException( + sprintf('Could not find a configuration file in any of these paths: %s', implode(',', $paths)) + ); + } + + private function parse(string $path): Configuration + { + $dom = new DOMDocument; + $dom->load($path); + + set_error_handler(function (int $number, string $error) use ($path) { + throw new RuntimeException( + sprintf("The configuration file %s is invalid.\n%s", $path, $error) + ); + }); + $dom->schemaValidate(__DIR__ . '/generator.xsd'); + restore_error_handler(); + + $xml = new SimpleXMLElement(file_get_contents($path)); + + return new Configuration( + (string)$xml['format'], + $this->getSources($xml), + ); + } + + /** + * @return Source[] + */ + private function getSources(SimpleXMLElement $xml): array + { + return array_map(function (SimpleXMLElement $source): Source { + return new Source( + (string)$source['path'], + $this->getStandards($source) + ); + }, $xml->xpath('source')); + } + + /** + * @return Standard[] + */ + private function getStandards(SimpleXMLElement $xml): array + { + return array_map(function (SimpleXMLElement $standard): Standard { + return new Standard( + (string)$standard['path'] + ); + }, $xml->xpath('standard')); + } +} diff --git a/src/Configuration/generator.xsd b/src/Configuration/generator.xsd new file mode 100644 index 0000000..fe6c4af --- /dev/null +++ b/src/Configuration/generator.xsd @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + <xs:element name="generator"> + <xs:complexType> + <xs:sequence> + <xs:element name="source" maxOccurs="unbounded"> + <xs:complexType> + <xs:sequence> + <xs:element name="standard" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="path" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="path" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="format" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/src/Generator/MarkdownGenerator.php b/src/Generator/MarkdownGenerator.php index b329433..2baaa73 100644 --- a/src/Generator/MarkdownGenerator.php +++ b/src/Generator/MarkdownGenerator.php @@ -27,8 +27,8 @@ public function createSniffDoc(Sniff $sniff): string {$this->getViolations($sniff->getViolations())} MD; - $sniffDoc = preg_replace('/\n{3,}/', "\n\n",$sniffDoc); - return preg_replace('/\n{2,}$/', "\n",$sniffDoc); + $sniffDoc = preg_replace('/\n{3,}/', "\n\n", $sniffDoc); + return preg_replace('/\n{2,}$/', "\n", $sniffDoc); } private function getDescription(Sniff $sniff): string diff --git a/src/Handler/GenerateHandler.php b/src/Handler/GenerateHandler.php index 8ebedf8..2aa7e83 100644 --- a/src/Handler/GenerateHandler.php +++ b/src/Handler/GenerateHandler.php @@ -3,7 +3,8 @@ namespace App\Handler; -use App\CodeRepository\CodeRepository; +use App\CodeRepository\CodeRepositoryFactory; +use App\Configuration\ConfigurationRepository; use App\Generator\Generator; use App\SniffFinder\SniffFinder; use App\Value\Folder; @@ -11,15 +12,22 @@ class GenerateHandler { - private CodeRepository $codeRepository; + private CodeRepositoryFactory $codeRepositoryFactory; private Generator $generator; private SniffFinder $sniffFinder; + private ConfigurationRepository $configRepo; - public function __construct(CodeRepository $codeRepository, Generator $generator, SniffFinder $sniffFinder) + public function __construct( + CodeRepositoryFactory $codeRepositoryFactory, + Generator $generator, + SniffFinder $sniffFinder, + ConfigurationRepository $configRepo + ) { - $this->codeRepository = $codeRepository; + $this->codeRepositoryFactory = $codeRepositoryFactory; $this->generator = $generator; $this->sniffFinder = $sniffFinder; + $this->configRepo = $configRepo; } /** @@ -27,27 +35,32 @@ public function __construct(CodeRepository $codeRepository, Generator $generator */ public function handle(string $sniffPath = null): iterable { - $repoName = 'PHPCompatibility/PHPCompatibility'; - $repoPath = $this->codeRepository->downloadCode($repoName); + $config = $this->configRepo->getConfig(); $filesystem = new Filesystem(); - $standardPath = new Folder($repoPath . 'PHPCompatibility/'); - yield "Searching for sniffs..."; + foreach ($config->getSources() as $source) { + $codeRepository = $this->codeRepositoryFactory->fromType($source->getType()); + $sourceFolder = $codeRepository->getFolder($source); + foreach ($source->getStandards() as $standard) { + $standardFolder = new Folder($sourceFolder . $standard->getPath() . '/'); + yield "Searching for sniffs in {$standardFolder}..."; - if ($sniffPath !== null) { - $sniffs = [$this->sniffFinder->getSniff($standardPath, $sniffPath)]; - } else { - $sniffs = $this->sniffFinder->getSniffs($standardPath); - } + if ($sniffPath !== null) { + $sniffs = [$this->sniffFinder->getSniff($standardFolder, $sourceFolder, $sniffPath)]; + } else { + $sniffs = $this->sniffFinder->getSniffs($standardFolder, $sourceFolder); + } - foreach ($sniffs as $sniff) { - $markdownPath = $this->sniffCodeToMarkdownPath($sniff->getCode()); - $filesystem->dumpFile( - // TODO: perhaps we can move this logic to the the sniff class - $markdownPath, - $this->generator->createSniffDoc($sniff) - ); - yield "Created file: {$markdownPath}"; + foreach ($sniffs as $sniff) { + $markdownPath = $this->sniffCodeToMarkdownPath($sniff->getCode()); + $filesystem->dumpFile( + // TODO: perhaps we can move this logic to the the sniff class + $markdownPath, + $this->generator->createSniffDoc($sniff) + ); + yield "Created file: {$markdownPath}"; + } + } } } diff --git a/src/Parser/SniffParser.php b/src/Parser/SniffParser.php index d8e4565..11973b6 100644 --- a/src/Parser/SniffParser.php +++ b/src/Parser/SniffParser.php @@ -206,7 +206,8 @@ private function getUrls(ReflectionClass $classInfo, array $xmlUrls): UrlList ->getTagsByName('link'); $urls = array_map(function (string $url) { - return new Url($url); + preg_match('/(http[^ ]+)/', $url, $matches); + return new Url($matches[0]); }, $links); return new UrlList(array_merge($urls, $xmlUrls)); diff --git a/src/SniffFinder/FilesystemSniffFinder.php b/src/SniffFinder/FilesystemSniffFinder.php index 58f744f..ee4bddf 100644 --- a/src/SniffFinder/FilesystemSniffFinder.php +++ b/src/SniffFinder/FilesystemSniffFinder.php @@ -18,21 +18,18 @@ class FilesystemSniffFinder implements SniffFinder { - public function getSniff(Folder $folder, string $sniffPath): Sniff + public function getSniff(Folder $standardFolder, Folder $sourceFolder, string $sniffPath): Sniff { $parser = new SniffParser(); - $projectSourceLocator = $this->createProjectSourceLocator($folder); + $projectSourceLocator = $this->createProjectSourceLocator($sourceFolder); return $parser->parse($sniffPath, $projectSourceLocator); } - public function getSniffs(Folder $folder): iterable + private function createProjectSourceLocator(Folder $folder): SourceLocator { - $parser = new SniffParser(); - $globSniffs = new GlobIterator($folder->getPath() . 'Sniffs/*/*Sniff.php'); - $projectSourceLocator = $this->createProjectSourceLocator($folder); - foreach ($globSniffs as $fileInfo) { - yield $parser->parse($fileInfo->getPathname(), $projectSourceLocator); - } + $astLocator = (new BetterReflection())->astLocator(); + $fileInfoIterator = $this->recursiveSearch($folder); + return new FileIteratorSourceLocator($fileInfoIterator, $astLocator); } /** @@ -47,10 +44,13 @@ private function recursiveSearch(Folder $folder): Iterator }); } - private function createProjectSourceLocator(Folder $folder): SourceLocator + public function getSniffs(Folder $standardFolder, Folder $sourceFolder): iterable { - $astLocator = (new BetterReflection())->astLocator(); - $fileInfoIterator = $this->recursiveSearch($folder); - return new FileIteratorSourceLocator($fileInfoIterator, $astLocator); + $parser = new SniffParser(); + $globSniffs = new GlobIterator($standardFolder->getPath() . 'Sniffs/*/*Sniff.php'); + $projectSourceLocator = $this->createProjectSourceLocator($sourceFolder); + foreach ($globSniffs as $fileInfo) { + yield $parser->parse($fileInfo->getPathname(), $projectSourceLocator); + } } } diff --git a/src/SniffFinder/SniffFinder.php b/src/SniffFinder/SniffFinder.php index c512125..e19088b 100644 --- a/src/SniffFinder/SniffFinder.php +++ b/src/SniffFinder/SniffFinder.php @@ -8,10 +8,10 @@ interface SniffFinder { - public function getSniff(Folder $folder, string $sniffPath): Sniff; + public function getSniff(Folder $standardFolder, Folder $sourceFolder, string $sniffPath): Sniff; /** * @return iterable<Sniff> */ - public function getSniffs(Folder $folder): iterable; + public function getSniffs(Folder $standardFolder, Folder $sourceFolder): iterable; } diff --git a/src/Value/Diff.php b/src/Value/Diff.php index 03e1f53..b991ae9 100644 --- a/src/Value/Diff.php +++ b/src/Value/Diff.php @@ -5,7 +5,7 @@ use Assert\Assert; -class Diff +final class Diff { private string $before; private string $after; diff --git a/src/Value/Folder.php b/src/Value/Folder.php index 76fdb84..e14fd2d 100644 --- a/src/Value/Folder.php +++ b/src/Value/Folder.php @@ -5,7 +5,7 @@ use Assert\Assert; -class Folder +final class Folder { private string $path; @@ -17,7 +17,7 @@ public function __construct(string $path) $this->path = $path; } - public function __toString() + public function __toString(): string { return $this->getPath(); } diff --git a/src/Value/Property.php b/src/Value/Property.php index 80856dc..1c017c4 100644 --- a/src/Value/Property.php +++ b/src/Value/Property.php @@ -5,7 +5,7 @@ use Assert\Assert; -class Property +final class Property { private string $name; private string $type; diff --git a/src/Value/Sniff.php b/src/Value/Sniff.php index deb666d..c277762 100644 --- a/src/Value/Sniff.php +++ b/src/Value/Sniff.php @@ -5,7 +5,7 @@ use Assert\Assert; -class Sniff +final class Sniff { private string $code; private string $docblock; diff --git a/src/Value/Url.php b/src/Value/Url.php index 92ed716..bbe8ebb 100644 --- a/src/Value/Url.php +++ b/src/Value/Url.php @@ -5,19 +5,19 @@ use Assert\Assert; -class Url +final class Url { private string $url; public function __construct(string $url) { Assert::that($url) - ->url(); + ->url('Not a valid URL: ' . $url); $this->url = $url; } - public function __toString() + public function __toString(): string { return $this->getUrl(); } diff --git a/src/Value/UrlList.php b/src/Value/UrlList.php index c852847..1fbd5e2 100644 --- a/src/Value/UrlList.php +++ b/src/Value/UrlList.php @@ -6,7 +6,7 @@ /** * Collection of unique URLs. */ -class UrlList +final class UrlList { /** * @var Url[] diff --git a/src/Value/Violation.php b/src/Value/Violation.php index 91ca7e5..29e4912 100644 --- a/src/Value/Violation.php +++ b/src/Value/Violation.php @@ -5,7 +5,7 @@ use Assert\Assert; -class Violation +final class Violation { private string $code; private string $description; diff --git a/tests/CodeRepository/CodeRepositoryFactoryTest.php b/tests/CodeRepository/CodeRepositoryFactoryTest.php new file mode 100644 index 0000000..932af42 --- /dev/null +++ b/tests/CodeRepository/CodeRepositoryFactoryTest.php @@ -0,0 +1,40 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\CodeRepository; + +use App\CodeRepository\CodeRepositoryFactory; +use App\CodeRepository\GitCodeRepository; +use App\CodeRepository\LocalCodeRepository; +use App\Configuration\Value\Source; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +/** @covers \App\CodeRepository\CodeRepositoryFactory */ +class CodeRepositoryFactoryTest extends TestCase +{ + /** @test */ + public function fromType_WithInvalid_ThrowException() + { + $this->expectException(InvalidArgumentException::class); + (new CodeRepositoryFactory)->fromType('INVALID'); + } + + /** @test */ + public function fromType_WithGit_ReturnGitImplementation() + { + self::assertInstanceOf( + GitCodeRepository::class, + (new CodeRepositoryFactory)->fromType(Source::TYPE_GIT) + ); + } + + /** @test */ + public function fromType_WithLocal_ReturnLocalImplementation() + { + self::assertInstanceOf( + LocalCodeRepository::class, + (new CodeRepositoryFactory)->fromType(Source::TYPE_LOCAL) + ); + } +} diff --git a/tests/CodeRepository/GitCodeRepositoryTest.php b/tests/CodeRepository/GitCodeRepositoryTest.php new file mode 100644 index 0000000..4798400 --- /dev/null +++ b/tests/CodeRepository/GitCodeRepositoryTest.php @@ -0,0 +1,62 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\CodeRepository; + +use App\CodeRepository\GitCodeRepository; +use App\Configuration\Value\Source; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Exception\RuntimeException; + +/** @covers \App\CodeRepository\GitCodeRepository */ +class GitCodeRepositoryTest extends TestCase +{ + private const LOCAL_GIT_PATH = 'var/tests/repo/Standard.git'; + private GitCodeRepository $codeRepo; + + /** @test */ + public function getFolder_WithMissingPath_ThrowException() + { + $this->expectException(RuntimeException::class); + $sourcePath = 'var/repos/MISSING.git'; + $this->codeRepo->getFolder(new Source($sourcePath, [])); + } + + /** @test */ + public function getFolder_WithGitPath_InstallComposer() + { + $this->createGitRepo(); + (new Filesystem())->remove('var/repos/Standard'); // force fresh clone + + $this->codeRepo->getFolder(new Source(self::LOCAL_GIT_PATH, [])); + self::assertFileExists('var/repos/Standard/composer.lock'); + } + + private function createGitRepo(): void + { + $fs = new Filesystem; + $gitPath = self::LOCAL_GIT_PATH; + $fs->mkdir($gitPath); + $fs->dumpFile($gitPath . '/composer.json', '{}'); + if (getenv('IS_CI_TEST')) { + `git config --global user.email "you@example.com"`; + `git config --global user.name "Your Name"`; + } + `cd {$gitPath} && git init && git add composer.json && git commit -m "Init"`; + } + + /** @test */ + public function getFolder_WithExistingGitClone_InstallComposer() + { + $this->createGitRepo(); + + $this->codeRepo->getFolder(new Source(self::LOCAL_GIT_PATH, [])); + self::assertFileExists('var/repos/Standard/composer.lock'); + } + + protected function setUp(): void + { + $this->codeRepo = new GitCodeRepository(); + } +} diff --git a/tests/CodeRepository/LocalCodeRepositoryTest.php b/tests/CodeRepository/LocalCodeRepositoryTest.php new file mode 100644 index 0000000..6071176 --- /dev/null +++ b/tests/CodeRepository/LocalCodeRepositoryTest.php @@ -0,0 +1,43 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\CodeRepository; + +use App\CodeRepository\LocalCodeRepository; +use App\Configuration\Value\Source; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Exception\RuntimeException; + +/** @covers \App\CodeRepository\LocalCodeRepository */ +class LocalCodeRepositoryTest extends TestCase +{ + private const LOCAL_PATH = 'var/repos/Standard'; + private LocalCodeRepository $codeRepo; + + /** @test */ + public function getFolder_WithMissingPath_ThrowException() + { + $this->expectException(RuntimeException::class); + $sourcePath = 'var/repos/MISSING'; + $this->codeRepo->getFolder(new Source($sourcePath, [])); + } + + /** @test */ + public function getFolder_WithExistingPath_InstallComposer() + { + $fs = new Filesystem; + $sourcePath = self::LOCAL_PATH; + $fs->remove($sourcePath); + $fs->mkdir($sourcePath); + $fs->dumpFile($sourcePath . '/composer.json', '{}'); + + $this->codeRepo->getFolder(new Source($sourcePath, [])); + self::assertFileExists('var/repos/Standard/composer.lock'); + } + + protected function setUp(): void + { + $this->codeRepo = new LocalCodeRepository(); + } +} diff --git a/tests/Configuration/Value/ConfigurationTest.php b/tests/Configuration/Value/ConfigurationTest.php new file mode 100644 index 0000000..bb8b7b1 --- /dev/null +++ b/tests/Configuration/Value/ConfigurationTest.php @@ -0,0 +1,51 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\Configuration\Value; + +use App\Configuration\Value\Configuration; +use App\Configuration\Value\Source; +use PHPUnit\Framework\TestCase; + +/** @covers \App\Configuration\Value\Configuration */ +class ConfigurationTest extends TestCase +{ + private const FORMAT = 'markdown'; + /** + * @var Source[] + */ + private static array $SOURCES; + + public static function setUpBeforeClass(): void + { + self::$SOURCES = [ + new Source('path/to/source', []) + ]; + } + + /** @test */ + public function getSources() + { + self::assertEquals( + self::$SOURCES, + $this->createValidVO()->getSources(), + ); + } + + private function createValidVO(): Configuration + { + return new Configuration( + self::FORMAT, + self::$SOURCES + ); + } + + /** @test */ + public function getFormat() + { + self::assertEquals( + self::FORMAT, + $this->createValidVO()->getFormat(), + ); + } +} diff --git a/tests/Configuration/Value/SourceTest.php b/tests/Configuration/Value/SourceTest.php new file mode 100644 index 0000000..e35bd13 --- /dev/null +++ b/tests/Configuration/Value/SourceTest.php @@ -0,0 +1,56 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\Configuration\Value; + +use App\Configuration\Value\Source; +use App\Configuration\Value\Standard; +use App\Value\Folder; +use PHPUnit\Framework\TestCase; + +/** @covers \App\Configuration\Value\Source */ +class SourceTest extends TestCase +{ + private const SOURCE_PATH = 'path/to/source'; + private const STANDARD_PATH = 'path/to/standard'; + + /** @test */ + public function getStandards() + { + self::assertEquals( + [ + new Standard(self::STANDARD_PATH) + ], + (new Source(self::SOURCE_PATH, [ + new Standard(self::STANDARD_PATH) + ]))->getStandards() + ); + } + + /** @test */ + public function getLocalFolder_WithGit_PrependDownloadPath() + { + self::assertEquals( + new Folder('var/repos/repo/'), + (new Source('path/to/repo.git', []))->getLocalFolder() + ); + } + + /** @test */ + public function getType_WithGit_ReturnGit() + { + self::assertEquals( + Source::TYPE_GIT, + (new Source('path/to/repo.git', []))->getType() + ); + } + + /** @test */ + public function getPath() + { + self::assertEquals( + self::SOURCE_PATH, + (new Source(self::SOURCE_PATH, []))->getPath() + ); + } +} diff --git a/tests/Configuration/Value/StandardTest.php b/tests/Configuration/Value/StandardTest.php new file mode 100644 index 0000000..23e3bb9 --- /dev/null +++ b/tests/Configuration/Value/StandardTest.php @@ -0,0 +1,30 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\Configuration\Value; + +use App\Configuration\Value\Standard; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +/** @covers \App\Configuration\Value\Standard */ +class StandardTest extends TestCase +{ + private const STANDARD_PATH = 'path/to/standard'; + + /** @test */ + public function construct_WithBlankPath_ThrowException() + { + $this->expectException(InvalidArgumentException::class); + new Standard(''); + } + + /** @test */ + public function getPath() + { + self::assertEquals( + self::STANDARD_PATH, + (new Standard(self::STANDARD_PATH))->getPath() + ); + } +} diff --git a/tests/Configuration/XmlConfigurationRepositoryTest.php b/tests/Configuration/XmlConfigurationRepositoryTest.php new file mode 100644 index 0000000..a452182 --- /dev/null +++ b/tests/Configuration/XmlConfigurationRepositoryTest.php @@ -0,0 +1,106 @@ +<?php +declare(strict_types=1); + +namespace App\Tests\Configuration; + +use App\Configuration\Value\Configuration; +use App\Configuration\Value\Source; +use App\Configuration\Value\Standard; +use App\Configuration\XmlConfigurationRepository; +use App\Value\Folder; +use PHPUnit\Framework\TestCase; +use RuntimeException; +use Symfony\Component\Filesystem\Filesystem; + +/** @covers \App\Configuration\XmlConfigurationRepository */ +class XmlConfigurationRepositoryTest extends TestCase +{ + const XML_FILE_PATH = 'var/tests/generator.xml'; + const XML_DIST_FILE_PATH = 'var/tests/generator.xml.dist'; + private XmlConfigurationRepository $repo; + + /** @test */ + public function getConfig_WithMissingFile_ThrowException() + { + $this->expectException(RuntimeException::class); + $this->repo->getConfig(); + } + + /** @test */ + public function getConfig_WithInvalidSchema_ThrowException() + { + $this->expectException(RuntimeException::class); + + $xmlContent = + '<?xml version="1.0" encoding="UTF-8"?> + <generator> + </generator>'; + + (new Filesystem)->dumpFile(self::XML_FILE_PATH, $xmlContent); + + $this->repo->getConfig(); + } + + /** @test */ + public function getConfig_WithBothFiles_PickNonDist() + { + $xmlContent = + '<?xml version="1.0" encoding="UTF-8"?> + <generator format="markdown"> + <source path="../"> + <standard path="Xml" /> + </source> + </generator>'; + + (new Filesystem)->dumpFile(self::XML_FILE_PATH, $xmlContent); + + $xmlDistContent = + '<generator format="markdown"> + <source path="../"> + <standard path="Dist" /> + </source> + </generator>'; + + (new Filesystem)->dumpFile(self::XML_DIST_FILE_PATH, $xmlDistContent); + + $config = $this->repo->getConfig(); + self::assertEquals( + 'Xml', + $config->getSources()[0]->getStandards()[0]->getPath() + ); + } + + /** @test */ + public function getConfig_WithValidFile_ReturnConfiguration() + { + $xmlContent = + '<?xml version="1.0" encoding="UTF-8"?> + <generator format="markdown"> + <source path="path/to/code"> + <standard path="Standard" /> + </source> + </generator>'; + + (new Filesystem)->dumpFile(self::XML_FILE_PATH, $xmlContent); + + self::assertEquals( + new Configuration( + 'markdown', + [ + new Source('path/to/code', [ + new Standard('Standard') + ]), + ] + ), + $this->repo->getConfig() + ); + } + + protected function setUp(): void + { + $fs = new Filesystem; + $fs->remove(self::XML_DIST_FILE_PATH); + $fs->remove(self::XML_FILE_PATH); + $this->repo = new XmlConfigurationRepository(new Folder('var/tests/')); + } +} diff --git a/tests/Handler/GenerateHandlerTest.php b/tests/Handler/GenerateHandlerTest.php index eb92c2a..9f46cb8 100644 --- a/tests/Handler/GenerateHandlerTest.php +++ b/tests/Handler/GenerateHandlerTest.php @@ -4,6 +4,11 @@ namespace App\Tests\Handler; use App\CodeRepository\CodeRepository; +use App\CodeRepository\CodeRepositoryFactory; +use App\Configuration\ConfigurationRepository; +use App\Configuration\Value\Configuration; +use App\Configuration\Value\Source; +use App\Configuration\Value\Standard; use App\Generator\Generator; use App\Handler\GenerateHandler; use App\SniffFinder\SniffFinder; @@ -23,9 +28,9 @@ class GenerateHandlerTest extends TestCase */ private GenerateHandler $handler; /** - * @var CodeRepository|MockObject + * @var CodeRepositoryFactory|MockObject */ - private $codeRepository; + private $codeRepositoryFactory; /** * @var Generator|MockObject */ @@ -34,11 +39,18 @@ class GenerateHandlerTest extends TestCase * @var SniffFinder|MockObject */ private $sniffFinder; + /** + * @var ConfigurationRepository|MockObject + */ + private $configRepo; /** @test */ public function handle_WithoutArguments_CreatesFile() { - $this->codeRepository->method('downloadCode')->willReturn(new Folder('var/tests/')); + $codeRepository = $this->createMock(CodeRepository::class); + $codeRepository->method('getFolder')->willReturn(new Folder('var/tests/')); + $this->codeRepositoryFactory->method('fromType')->willReturn($codeRepository); + $sniffs = $this->createSniffs(['First', 'Second']); $this->sniffFinder->method('getSniffs')->willReturn($sniffs); $this->generator->method('createSniffDoc')->withConsecutive([$sniffs[0]], [$sniffs[1]]); @@ -48,7 +60,7 @@ public function handle_WithoutArguments_CreatesFile() self::assertEquals( [ - 'Searching for sniffs...', + 'Searching for sniffs in var/tests/Standard/...', 'Created file: var/markdown/Standard/Category/First.md', 'Created file: var/markdown/Standard/Category/Second.md' ], @@ -89,7 +101,9 @@ private function createSniff(string $name): Sniff /** @test */ public function handle_WithSniffPath_CreatesSingleFile() { - $this->codeRepository->method('downloadCode')->willReturn(new Folder('var/tests/')); + $codeRepository = $this->createMock(CodeRepository::class); + $codeRepository->method('getFolder')->willReturn(new Folder('var/tests/')); + $this->codeRepositoryFactory->method('fromType')->willReturn($codeRepository); $this->sniffFinder->method('getSniff')->willReturn($this->createSniff('First')); /** @var \Generator $messages */ @@ -97,7 +111,7 @@ public function handle_WithSniffPath_CreatesSingleFile() self::assertEquals( [ - 'Searching for sniffs...', + 'Searching for sniffs in var/tests/Standard/...', 'Created file: var/markdown/Standard/Category/First.md', ], iterator_to_array($messages) @@ -108,14 +122,25 @@ protected function setUp(): void { (new Filesystem())->remove('var/markdown/Standard'); - $this->codeRepository = $this->createMock(CodeRepository::class); + $this->codeRepositoryFactory = $this->createMock(CodeRepositoryFactory::class); $this->generator = $this->createMock(Generator::class); $this->sniffFinder = $this->createMock(SniffFinder::class); + $this->configRepo = $this->createMock(ConfigurationRepository::class); + + $this->configRepo->method('getConfig')->willReturn(new Configuration( + 'markdown', + [ + new Source('../Standard', [ + new Standard('Standard') + ]) + ] + )); $this->handler = new GenerateHandler( - $this->codeRepository, + $this->codeRepositoryFactory, $this->generator, - $this->sniffFinder + $this->sniffFinder, + $this->configRepo ); } } diff --git a/tests/SniffFinder/FilesystemSniffFinderTest.php b/tests/SniffFinder/FilesystemSniffFinderTest.php index e161aff..457f88d 100644 --- a/tests/SniffFinder/FilesystemSniffFinderTest.php +++ b/tests/SniffFinder/FilesystemSniffFinderTest.php @@ -32,6 +32,9 @@ public function getSniffs() $sniffs = $this->finder->getSniffs( new Folder( 'var/tests/src/Standard/' + ), + new Folder( + 'var/tests/' ) ); self::assertEquals( @@ -114,6 +117,9 @@ public function getSniff() new Folder( 'var/tests/src/Standard/' ), + new Folder( + 'var/tests/' + ), self::PHP_SNIFF_PATH ) ); diff --git a/var/repos/.gitkeep b/var/repos/.gitkeep deleted file mode 100644 index e69de29..0000000