From 1509c419ab7c6049e559d6f328032aec94d6845e Mon Sep 17 00:00:00 2001 From: Bill Ruddock Date: Mon, 8 Nov 2021 05:06:18 +0000 Subject: [PATCH 1/4] Use PHPUnit 7 --- composer.json | 2 +- composer.lock | 807 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 584 insertions(+), 225 deletions(-) diff --git a/composer.json b/composer.json index 63342be..516a761 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "phpcompatibility/php-compatibility": "*" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~7.0" }, "license": "BSD", "minimum-stability": "dev", diff --git a/composer.lock b/composer.lock index ddcef53..f6c9409 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": "6ef964c03350fb19e3af98a69a2e096c", + "content-hash": "f4d89abc00baecffa69bc2de467e169f", "packages": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -248,6 +248,162 @@ ], "time": "2020-11-10T18:47:58+00:00" }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -398,33 +554,33 @@ }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -457,43 +613,44 @@ "spy", "stub" ], - "time": "2020-03-05T15:02:03+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "2.2.4", + "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -508,7 +665,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -519,29 +676,32 @@ "testing", "xunit" ], - "time": "2015-10-06T15:47:00+00:00" + "time": "2018-10-31T16:06:48+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", + "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -556,7 +716,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -566,7 +726,13 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-07-19T06:46:01+00:00" }, { "name": "phpunit/php-text-template", @@ -611,28 +777,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -647,7 +813,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -656,33 +822,39 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.12", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" + "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", + "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -705,46 +877,64 @@ "keywords": [ "tokenizer" ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "abandoned": true, - "time": "2017-12-04T08:55:13+00:00" + "time": "2021-07-26T12:15:06+00:00" }, { "name": "phpunit/phpunit", - "version": "4.8.36", + "version": "7.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.2.2", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" }, "suggest": { - "phpunit/php-invoker": "~1.1" + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -752,7 +942,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.8.x-dev" + "dev-master": "7.5-dev" } }, "autoload": { @@ -778,38 +968,32 @@ "testing", "xunit" ], - "time": "2017-06-21T08:07:12+00:00" + "time": "2020-01-08T08:45:45+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -824,45 +1008,45 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "abandoned": true, - "time": "2015-10-02T06:51:40+00:00" + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "1.2.4", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": ">=7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -875,6 +1059,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -886,45 +1074,48 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], - "time": "2017-01-29T09:50:25+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "1.4.3", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -937,46 +1128,58 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-05-22T07:24:03+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "1.3.8", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1001,34 +1204,40 @@ "environment", "hhvm" ], - "time": "2016-08-18T05:49:44+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "1.2.2", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" + "php": ">=7.0", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1041,6 +1250,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1049,17 +1262,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1068,27 +1277,33 @@ "export", "exporter" ], - "time": "2016-06-17T09:04:28+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:47:53+00:00" }, { "name": "sebastian/global-state", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" @@ -1096,7 +1311,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1119,32 +1334,136 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:40:27+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "1.0.5", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1157,14 +1476,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1172,23 +1491,85 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-10-03T07:41:43+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:34:24+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "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" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/version", - "version": "1.0.6", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, + "require": { + "php": ">=5.6" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1207,7 +1588,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21T13:59:46+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1286,72 +1667,50 @@ "time": "2021-02-19T12:13:01+00:00" }, { - "name": "symfony/yaml", - "version": "v3.4.47", + "name": "theseer/tokenizer", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "88289caa3c166321883f67fe5130188ebbb47094" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", - "reference": "88289caa3c166321883f67fe5130188ebbb47094", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/theseer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", From 019fe6003e79c352810724c051f4a1403224a090 Mon Sep 17 00:00:00 2001 From: Bill Ruddock Date: Mon, 8 Nov 2021 05:10:49 +0000 Subject: [PATCH 2/4] Separate test assertions Use a separate test function for each function / class example. Also assert the messages that are given for each warning / error. --- .../Tests/Constraint/ArrayMatchesMessages.php | 161 ++ .../VariableAnalysisUnitTest.inc | 466 ----- .../VariableAnalysisUnitTest.php | 1632 ++++++++++++++--- phpunit.xml | 2 +- 4 files changed, 1546 insertions(+), 715 deletions(-) create mode 100644 VariableAnalysis/Tests/Constraint/ArrayMatchesMessages.php delete mode 100644 VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.inc diff --git a/VariableAnalysis/Tests/Constraint/ArrayMatchesMessages.php b/VariableAnalysis/Tests/Constraint/ArrayMatchesMessages.php new file mode 100644 index 0000000..b143cd4 --- /dev/null +++ b/VariableAnalysis/Tests/Constraint/ArrayMatchesMessages.php @@ -0,0 +1,161 @@ + + * $line = 2; + * $column = 10; + * [ + * $line => [ + * $column => [ + * [ + * 'message' => 'Unused function parameter $foo.', + * 'source' => 'VariableAnalysis.VariableAnalysis.VariableAnalysis.UnusedVariable', + * 'listener' => 'VariableAnalysis\Sniffs\VariableAnalysis\VariableAnalysisSniff', + * 'severity' => 5, + * 'fixable' => false + * ], + * ] + * ] + * ] + * + */ +class ArrayMatchesMessages extends \PHPUnit\Framework\Constraint\Constraint +{ + /** + * Expected messages as a list of [line, column, message] + * + * e.g.: + * + * + * $line = 2; + * $column = 10; + * [ + * [$line, $column, 'Unused function parameter $foo.'], + * ] + * + * + * @var array Expected messages + */ + private $messages; + + /** + * Constructor + * + * @param array $messages Expected list of messages as [line, column, message] + */ + public function __construct($messages) + { + parent::__construct(); + $this->messages = $this->normaliseExpectedMessages($messages); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return 'matches expected messages' . PHP_EOL . $this->exporter->export($this->messages); + } + + /** + * Evaluates the constraint for parameter $actual. + * + * @param array $actual PHPCodeSniffer warnings or errors list. + * + * @return bool True if all messages match, false otherwise. + */ + protected function matches($actual): bool + { + if (!\is_array($actual)) { + return false; + } + + $actualMessages = $this->extractMessages($actual); + + if (\array_keys($actualMessages) != \array_keys($this->messages)) { + return false; + } + foreach ($actualMessages as $line => $actualLine) { + $expectedLine = $this->messages[$line]; + if (\array_keys($actualLine) != \array_keys($expectedLine)) { + return false; + } + + foreach ($actualLine as $column => $actualColumn) { + $expectedColumn = $expectedLine[$column]; + if ($actualColumn != $expectedColumn) { + return false; + } + } + } + + return true; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $actual Evaluated warnings or errors from a PHPCodeSniffer file. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($actual): string + { + $actualMessages = $this->extractMessages($actual); + return $this->exporter->export($actualMessages) . ' ' . $this->toString(); + } + + private function extractMessages($actual) + { + $extractedMessages = []; + foreach ($actual as $line => $lineMessages) { + $extractedMessages[$line] = []; + foreach ($lineMessages as $column => $columnMessages) { + $sortedMessages = array_map( + function ($item) { + return $item['message']; + }, + $columnMessages + ); + sort($sortedMessages); + $extractedMessages[$line][$column] = $sortedMessages; + } + } + return $extractedMessages; + } + + private function normaliseExpectedMessages($messages) { + $result = []; + foreach ($messages as $line => $lineMessages) { + $result[$line] = []; + foreach ($lineMessages as $column => $columnMessages) { + $sortedMessages = array_values($columnMessages); + sort($sortedMessages); + $result[$line][$column] = $sortedMessages; + } + } + return $result; + } +} diff --git a/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.inc b/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.inc deleted file mode 100644 index 253a0ea..0000000 --- a/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.inc +++ /dev/null @@ -1,466 +0,0 @@ - $value1) { - echo "$key1 => $value1\n"; - } - echo "$key1 => $value1\n"; - foreach ($array as $key2 => &$value2) { - echo "$key2 => $value2\n"; - } - echo "$key2 => $value2\n"; - foreach ($array as $element3) { - } - foreach ($array as &$element4) { - } - foreach ($array as $key3 => $value3) { - } - foreach ($array as $key4 => &$value4) { - } -} - -function function_with_defined_foreach() { - $array = array(); - foreach ($array as $element1) { - echo $element1; - } - echo $element1; - foreach ($array as &$element2) { - echo $element2; - } - echo $element2; - foreach ($array as $key1 => $value1) { - echo "$key1 => $value1\n"; - } - echo "$key1 => $value1\n"; - foreach ($array as $key2 => &$value2) { - echo "$key2 => $value2\n"; - } - echo "$key2 => $value2\n"; - foreach ($array as $element3) { - } - foreach ($array as &$element4) { - } - foreach ($array as $key3 => $value3) { - } - foreach ($array as $key4 => &$value4) { - } -} - -class ClassWithoutMembers { - function method_without_param() { - echo $var; - echo "xxx $var xxx"; - echo "xxx {$var} xxx"; - echo "xxx $var $var2 xxx"; - echo "xxx {$var} {$var2} xxx"; - func($var); - func(12, $var); - func($var, 12); - func(12, $var, 12); - $var = 'set the var'; - echo $var; - echo "xxx $var xxx"; - echo "xxx {$var} xxx"; - echo "xxx $var $var2 xxx"; - echo "xxx {$var} {$var2} xxx"; - func($var); - func(12, $var); - func($var, 12); - func(12, $var, 12); - $this->method_with_member_var(); - return $var; - } - - function method_with_param($param) { - echo $param; - echo "xxx $param xxx"; - echo "xxx {$param} xxx"; - $param = 'set the param'; - echo $param; - echo "xxx $param xxx"; - echo "xxx {$param} xxx"; - $this->method_with_member_var(); - return $param; - } - - function method_with_member_var() { - echo $this->member_var; - echo self::$static_member_var; - } -} - -class ClassWithMembers { - public $member_var; - static $static_member_var; - - function method_with_member_var() { - echo $this->member_var; - echo $this->no_such_member_var; - echo self::$static_member_var; - echo self::$no_such_static_member_var; - echo SomeOtherClass::$external_static_member_var; - } -} - -function function_with_this_outside_class() { - return $this->whatever(); -} - -function function_with_static_members_outside_class() { - echo SomeOtherClass::$external_static_member_var; - return self::$whatever; -} - -function function_with_late_static_binding_outside_class() { - echo static::$whatever; -} - -function function_with_closure($outer_param) { - $outer_var = 1; - $outer_var2 = 2; - array_map(function ($inner_param) { - $inner_var = 1; - echo $outer_param; - echo $inner_param; - echo $outer_var; - echo $outer_var2; - echo $inner_var; - }, array()); - array_map(function () use ($outer_var, $outer_var3, &$outer_param) { - $inner_var2 = 2; - echo $outer_param; - echo $inner_param; - echo $outer_var; - echo $outer_var2; - echo $outer_var3; - echo $inner_var; - echo $inner_var2; - }, array()); - echo $outer_var; - echo $outer_var2; - echo $outer_var3; - echo $inner_param; - echo $inner_var; - echo $inner_var2; -} - -function &function_with_return_by_reference_and_param($param) { - echo $param; - return $param; -} - -function function_with_static_var() { - static $static1, $static_num = 12, $static_neg_num = -1.5, $static_string = 'abc', $static_string2 = "def", $static_define = MYDEFINE, $static_constant = MyClass::CONSTANT, $static2; - static $static_heredoc = <<$property = 'some value'; - $this -> $property = 'some value'; - $this->$undefined_property = 'some value'; - $this -> $undefined_property = 'some value'; - } - } - - function method_with_symbolic_ref_method() { - $methods = array('method_with_symbolic_ref_property'); - foreach ($methods as $method) { - $this->$method(); - $this -> $method(); - $this->$undefined_method(); - $this -> $undefined_method(); - } - } -} - -function function_with_pass_by_ref_assign_only_arg(&$return_value) { - $return_value = 42; -} - -class ClassWithLateStaticBinding { - static function method_with_late_static_binding($param) { - static::some_method($param); - static::some_method($var); - static::some_method(static::CONSTANT, $param); - } -} - -function function_with_literal_compact($param1, $param2, $param3, $param4) { - $var1 = 'value1'; - $var2 = 'value2'; - $var4 = 'value4'; - $squish = compact('var1'); - $squish = compact('var3'); - $squish = compact('param1'); - $squish = compact('var2', 'param3'); - $squish = compact(array('var4'), array('param4', 'var5')); - echo $squish; -} - -function function_with_expression_compact($param1, $param2, $param3, $param4) { - $var1 = "value1"; - $var2 = "value2"; - $var4 = "value4"; - $var6 = "value6"; - $var7 = "value7"; - $var8 = "value8"; - $var9 = "value9"; - $squish = compact("var1"); - $squish = compact("var3"); - $squish = compact("param1"); - $squish = compact("var2", "param3"); - $squish = compact(array("var4"), array("param4", "var5")); - $squish = compact($var6); - $squish = compact("var" . "7"); - $squish = compact("blah $var8"); - $squish = compact("$var9"); - echo $squish; -} diff --git a/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.php b/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.php index 2be4db9..18acabc 100644 --- a/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.php +++ b/VariableAnalysis/Tests/VariableAnalysis/VariableAnalysisUnitTest.php @@ -2,7 +2,15 @@ namespace VariableAnalysis\Tests\VariableAnalysis; -use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; +use VariableAnalysis\Tests\Constraint\ArrayMatchesMessages; +use VariableAnalysis\Tests\Constraint\ContainsMessage; +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Exceptions\RuntimeException; +use PHP_CodeSniffer\Files\DummyFile; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Ruleset; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\InvalidArgumentHelper; /** * Unit test class for the VariableAnalysis sniff. @@ -20,267 +28,1395 @@ * @version Release: @package_version@ * @link http://pear.php.net/package/PHP_CodeSniffer */ -class VariableAnalysisUnitTest extends AbstractSniffUnitTest +class VariableAnalysisUnitTest extends TestCase { - private function _getWarningAndErrorList() - { - // This is a maintainence nightmare. - // Value of a line is either: - // - an int: the number of warnings. - // - an array: the number of warnings and number of errors. - // This is chosen because we mostly warn and because maintaining _two_ - // separate lists of line numbers would drive me insane. - // - // All the fiddling with $base is to make each line number relative - // to the line number of the function the line is in, which in turn - // is relative to the line number of the previous function. - // - // This makes adding new tests only moderately painful rather than - // a total clusterfuck of alterations. - $base = 0; - return [ - // function_without_param() line (+3) - ($base += 3) => [0, 0], - ($base + 1) => [0, 1], // $var - ($base + 2) => [0, 1], // $var - ($base + 3) => [0, 1], // {$var} - ($base + 4) => [0, 1], // ${var} - ($base + 5) => [0, 2], // $var $var2 - ($base + 6) => [0, 2], // $var $var2 - ($base + 7) => [0, 1], // $var - ($base + 8) => [0, 1], // $var - ($base + 9) => [0, 1], // $var - ($base + 10) => [0, 1], // $var - ($base + 15) => [0, 1], // $var2 - ($base + 16) => [0, 1], // $var2 - // function_with_param() line (+24) - // no warnings. - ($base += 24) => 0, - // function_with_default_defined_param() line (+11) - ($base += 11) => 0, - ($base + 0) => 1, // $unused - // function_with_default_null_param() line (+11) - ($base += 11) => 0, - ($base + 0) => 1, // $unused - // function_with_global_var() line (+11) - ($base += 11) => 0, - ($base + 1) => 1, // $unused - ($base + 4) => [0,1], // $var3 - // function_with_undefined_foreach() line (+8) - ($base += 8) => 0, - ($base + 1) => [0, 1], // $array - ($base + 5) => [0, 1], // $array - ($base + 9) => [0, 1], // $array - ($base + 13) => [0, 1], // $array - ($base + 17) => [1, 1], // $array, $element3 - ($base + 19) => [1, 1], // $array, $element4 - ($base + 21) => [2, 1], // $array, $key3, $value4 - ($base + 23) => [2, 1], // $array, $key4, $value4 - // function_with_defined_foreach() line (+27) - ($base += 27) => 0, - ($base + 18) => 1, // $element3 - ($base + 20) => 1, // $element4 - ($base + 22) => 2, // $key3, $value4 - ($base + 24) => 2, // $key4, $value4 - // ClassWithoutMembers->method_without_param() line (+29) - ($base += 29) => 0, - ($base + 1) => [0, 1], // $var - ($base + 2) => [0, 1], // $var - ($base + 3) => [0, 1], // $var - ($base + 4) => [0, 2], // $var $var2 - ($base + 5) => [0, 2], // $var $var2 - ($base + 6) => [0, 1], // $var - ($base + 7) => [0, 1], // $var - ($base + 8) => [0, 1], // $var - ($base + 9) => [0, 1], // $var - ($base + 14) => [0, 1], // $var2 - ($base + 15) => [0, 1], // $var2 - // ClassWithoutMembers->method_with_param() line (+24) - // no warnings. - ($base += 24) => 0, - // ClassWithoutMembers->method_with_member_var() line (+12) - // no warnings. - // We can't/don't inspect the class inheritence so we can't - // determine that these are undeclared: - // $this->member_var - // self::$static_member_var - ($base += 12) => 0, - ($base + 1) => [0, 1], // $this->member_var - //FIXME: should we check that - //($base + 1) => [0, 1], // self::$static_member_var - // ClassWithMembers->method_with_member_var() line (+10) - // no warnings. - // We can't/don't inspect the class inheritence so we can't - // determine that these are undeclared: - // $this->no_such_member_var - // self::$no_such_static_member_var - ($base += 10) => 0, - //FIXME: Static meber var is actually used? - //($base + 1) => [0, 1], // static $static_member_var; - ($base + 2) => [0, 1], // $this->no_such_member_var - // function_with_this_outside_class() line (+9) - ($base += 9) => 0, - ($base + 1) => [0, 1], // $this - // function_with_static_members_outside_class() line (+4) - ($base += 4) => 0, - ($base + 2) => array(0, 1), // self::$whatever - // function_with_late_static_binding_outside_class() line (+5) - ($base += 5) => 0, - ($base + 1) => array(0, 1), // static::$whatever - // function_with_closure() line (+4) - ($base += 4) => 0, - ($base + 5) => [0, 1], // $outer_param - ($base + 7) => [0, 1], // $outer_var - ($base + 8) => [0, 1], // $outer_var2 - ($base + 11) => [0, 1], // $outer_var3 - ($base + 14) => [0, 1], // $inner_param - ($base + 16) => [0, 1], // $outer_var2 - ($base + 17) => [0, 1], // $outer_var3 - ($base + 18) => [0, 1], // $inner_var - ($base + 23) => [0, 1], // $outer_var3 - ($base + 24) => [0, 1], // $inner_param - ($base + 25) => [0, 1], // $inner_var - ($base + 26) => [0, 1], // $inner_var2 - // function_with_return_by_reference_and_param() line (+29) - // no warnings. - ($base += 29) => 0, - // function_with_static_var() line (+5) - ($base += 5) => 0, - ($base + 1) => 5, // $static_neg_num, $static_string, $static_string2, - // $static_define, $static_constant - ($base + 13) => [0, 1], // $var - // function_with_pass_by_reference_param() line (+20) - // no warnings. - ($base += 20) => 0, - // function_with_pass_by_reference_calls() line (+4) - ($base += 4) => 0, - ($base + 1) => [0, 1], // $matches - ($base + 2) => [0, 1], // $needle - ($base + 3) => [0, 1], // $haystack - ($base + 5) => [0, 1], // $needle - ($base + 6) => [0, 1], // $haystack - ($base + 8) => [0, 1], // $needle - ($base + 9) => [0, 1], // $haystack - ($base + 15) => [0, 1], // $var3 - // function_with_try_catch() line (+22) - ($base += 22) => 0, - ($base + 1) => [0, 1], // $e - ($base + 5) => [0, 1], // $e - // ClassWithThisInsideClosure->method_with_this_inside_closure() line (+16) - ($base += 16) => 0, - ($base + 3) => 1, // $inner_param - ($base + 4) => 0, // $this - fine with ongoing PHP - ($base + 5) => 0, // $this - fine with ongoing PHP - // ClassWithSelfInsideClosure->method_with_self_inside_closure() line (+15) - ($base += 15) => 0, - ($base + 3) => 0, // $self::$static_member - // function_with_inline_assigns() line (+9) - ($base += 9) => 0, - ($base + 1) => [0, 1], // $var - ($base + 4) => [0, 1], // $var2 - // function_with_global_redeclarations() line (+11) - ($base += 11) => 0, - ($base + 5) => 1, // $bound - ($base + 12) => 1, // $param - ($base + 13) => 1, // $static - ($base + 14) => 1, // $bound - ($base + 15) => 1, // $local - ($base + 16) => 1, // $e - // function_with_static_redeclarations() line (+19) - ($base += 19) => 0, - ($base + 2) => 1, // $static - ($base + 5) => 1, // $bound - ($base + 12) => 1, // $param - ($base + 13) => 1, // $static - ($base + 14) => 1, // $bound - ($base + 15) => 1, // $local - ($base + 16) => 1, // $e - // function_with_catch_redeclarations() line (+19) - // no warnings. - ($base += 19) => 0, - // function_with_superglobals() line (+11) - ($base += 11) => 0, - ($base + 11) => [0, 1], // $var - // function_with_heredoc() line (+14) - ($base += 14) => 0, - ($base + 6) => [0, 1], // $var2 - ($base + 7) => [0, 1], // {$var2} - ($base + 8) => [0, 1], // ${var2} - ($base + 10) => [0, 1], // \\$var2 - // method_with_symbolic_ref_property() line (+17) - ($base += 17) => 0, - ($base + 5) => [0, 1], // $undefined_property - ($base + 6) => [0, 1], // $undefined_property - // method_with_symbolic_ref_method() line (+8) - ($base += 10) => 0, - ($base + 5) => [0, 1], // $undefined_method - ($base + 6) => [0, 1], // $undefined_method - // function_with_pass_by_ref_assign_only_arg() line (+11) - // no warnings. - ($base += 11) => 0, - // method_with_late_static_binding() line (+5) - ($base += 5) => 0, - ($base + 2) => [0, 1], // $var - // function_with_single_quoted_compact() line (+7) - ($base += 7) => 0, - ($base + 0) => 1, // unused $param2 - ($base + 5) => [0, 1], // undefined $var3 - ($base + 8) => [0, 1], // undefined $var5 - // function_with_expression_compact() line (+12) - ($base += 12) => 0, - ($base + 0) => 1, // unused $param2 - ($base + 5) => 1, // unused $var7 - ($base + 9) => [0, 1], // undefined $var3 - ($base + 12) => [0, 1], // undefined $var5 - ]; + // FIXME: These static variables are used but not detected. + // phpcs:disable VariableAnalysis.VariableAnalysis.VariableAnalysis.UnusedVariable + private static Config $config; + private static array $savedConfigSettings; + // phpcs:enable + + /** + * Setup config for this sniff. + * + * @beforeClass + * + * @return void + */ + public static function setUpBeforeClass() { + $config = new Config(); + $config->standards = ['VariableAnalysis']; + $config->sniffs = ['VariableAnalysis.VariableAnalysis.VariableAnalysis']; + self::$config = $config; } /** - * Returns the lines where errors should occur. + * Setup per test case. * - * The key of the array should represent the line number and the value - * should represent the number of errors that should occur on that line. + * @before * - * @return array(int => int) - */ - public function getErrorList() - { - $errorList = array(); - foreach ($this->_getWarningAndErrorList() as $line => $incidents) { - $errors = null; - if (is_array($incidents)) { - list ($warnings, $errors) = $incidents; - } - if (!empty($errors)) { - $errorList[$line] = $errors; - } + * @return void + */ + public function setUp() { + self::$savedConfigSettings = self::$config->getSettings(); + } + + /** + * Teardown per test. + * + * @after + * + * @return void + */ + public function tearDown() { + self::$config->setSettings(self::$savedConfigSettings); + } + + /** + * Create a dummy file for code under test + * + * @param string $contents Contents of code snippet under test. + * + * @return PHPCodeSniffer\Files\DummyFile + */ + private function dummyFile($contents) { + $ruleset = new Ruleset(self::$config); + $dummyFile = new DummyFile($contents, $ruleset, self::$config); + try { + $dummyFile->process(); + } catch (RuntimeException $e) { + $this->fail('Error processing dummy file: ' . $e->getMessage()); } - return $errorList; + return $dummyFile; } /** - * Returns the lines where warnings should occur. + * Insert code before and after assignment to variable(s) * - * The key of the array should represent the line number and the value - * should represent the number of warnings that should occur on that line. + * @param string $template Surrounding code to insert into. BEFORE and + * AFTER markers show where to insert $code such + * that it is before and after the assignment + * respectively. + * @param string $code The code to insert in place of the BEFORE and + * AFTER markers. * - * @return array(int => int) - */ - public function getWarningList() - { - $warningList = array(); - foreach ($this->_getWarningAndErrorList() as $line => $incidents) { - $warnings = null; - if (is_array($incidents)) { - list ($warnings, $errors) = $incidents; - } else { - $warnings = $incidents; - } - if (!empty($warnings)) { - $warningList[$line] = $warnings; + * @return [string] The result of inserting $code before and after + * respectively, i.e. [$before, $after] + */ + private function insertBeforeAndAfter($template, $code) { + $beforeAssign = str_replace( + 'AFTER', '', str_replace('BEFORE', $code, $template) + ); + $afterAssign = str_replace( + 'AFTER', $code, str_replace('BEFORE', '', $template) + ); + + return [$beforeAssign, $afterAssign]; + } + + /** + * Assert file warnings match the expected messages on given lines. + * + * @param PHPCodeSniffer\Files\File $file File under test. + * @param array $expected Expected warnings, each as [line, column, message] + * @param string $desc Additional description for the assertion. + */ + private function assertWarningsMatch(File $file, $expected, $desc = '') { + $desc = $desc == '' ? $desc : ' ' . $desc; + if (!\is_array($expected)) { + throw InvalidArgumentHelper::factory(2, 'array'); + } + + $constraint = new ArrayMatchesMessages($expected); + $this->assertThat($file->getWarnings(), $constraint, 'warnings' . $desc); + } + + /** + * Assert file errors match the expected messages on given lines. + * + * @param PHPCodeSniffer\Files\File $file File under test. + * @param array $expected Expected errors, each as [line, column, message] + * @param string $desc Additional description for the assertion. + */ + private function assertErrorsMatch(File $file, $expected, $desc = '') { + $desc = $desc == '' ? $desc : ' ' . $desc; + if (!\is_array($expected)) { + throw InvalidArgumentHelper::factory(2, 'array'); + } + + $constraint = new ArrayMatchesMessages($expected); + $this->assertThat($file->getErrors(), $constraint, 'errors' . $desc); + } + + /** + * Add line and column offset to expected messages. + * + * @param array $expectedMessages List of expected messages + * @param int $line Offset to code line. + * @param int $column Offset to code column. + * + * @return array expectedMessages with offsets applied. + */ + private function offset($expectedMessages, $line, $column) { + $result = []; + foreach ($expectedMessages as $y => $lineMessages) { + $result[$y + $line] = []; + foreach ($lineMessages as $x => $columnMessages) { + $result[$y + $line][$x + $column] = $columnMessages; } } - return $warningList; + return $result; + } + + /** + * Test checks for use of undefined variables in function with no parameters. + * + * @param string $code Code snippet under test + * @param array $errorsBefore Expected errors before $var is assigned. + * @param array $errorsAfter Optional. Expected errors after $var is + * assigned. Defaults to [] + * + * @dataProvider dataUndefinedVar + * + * @return void + */ + public function testFunctionNoParams($code, $errorsBefore, $errorsAfter = []) { + $template = <<<'CODE' +insertBeforeAndAfter($template, $code); + + $beforeFile = $this->dummyFile($beforeAssign); + $this->assertWarningsMatch($beforeFile, [], 'before assign'); + $this->assertErrorsMatch($beforeFile, $this->offset($errorsBefore, 2, 4), 'before assign'); + + $afterFile = $this->dummyFile($afterAssign); + $this->assertWarningsMatch($afterFile, [], 'after assign'); + $this->assertErrorsMatch($afterFile, $this->offset($errorsAfter, 4, 4), 'after assign'); + } + + /** + * Data provider for undefined variable sniff in function without param. + * + * @see testFunctionNoParams() + * @see testClassNoMembersMethodNoParams() + * + * @return array + */ + public function dataUndefinedVar() { + return [ + [ + 'echo $var;', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'echo "xxx $var xxx";', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'echo "xxx {$var} xxx";', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'echo "xxx ${var} xxx";', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'echo "xxx $var $var2 xxx";', + [ + 1 => [ + 6 => [ + 'Variable $var is undefined.', + 'Variable $var2 is undefined.' + ], + ], + ], + [ + 1 => [ 6 => ['Variable $var2 is undefined.'] ], + ], + ], + [ + 'echo "xxx {$var} {$var2} xxx";', + [ + 1 => [ + 6 => [ + 'Variable $var is undefined.', + 'Variable $var2 is undefined.' + ], + ], + ], + [ + 1 => [ 6 => ['Variable $var2 is undefined.'] ], + ], + ], + [ + 'func($var);', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'func(12, $var);', + [ + 1 => [ 10 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'func($var, 12);', + [ + 1 => [ 6 => ['Variable $var is undefined.'] ], + ], + ], + [ + 'func(12, $var, 12);', + [ + 1 => [ 10 => ['Variable $var is undefined.'] ], + ], + ], + ]; + } + + /** + * Test checks for use of undefined variables in function with parameter. + * + * @return void + */ + public function testFunctionWithParam() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks for unused variables in function with default param. + * + * @return void + */ + public function testFunctionWithDefaultDefinedParam() { + $code = <<<'CODE' +dummyFile($code); + + $this->assertWarningsMatch( + $file, + [ + 2 => [ 46 => ['Unused function parameter $unused.'] ], + ] + ); + } + + /** + * Test checks for unused variables in function with default null param. + * + * @return void + */ + public function testFunctionWithDefaultNullParam() { + $code = <<<'CODE' +dummyFile($code); + + $this->assertWarningsMatch( + $file, + [ + 2 => [ 43 => ['Unused function parameter $unused.'] ], + ] + ); + } + + /** + * Test checks for unused / undefined variables in function with global vars. + * + * @return void + */ + public function testFunctionWithGlobalVars() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 3 => [ 25 => ['Unused global variable $unused.'] ], + ] + ); + $this->assertErrorsMatch( + $file, + [ + 6 => [ 10 => ['Variable $var3 is undefined.'] ], + ] + ); + } + + /** + * Test checks for unused and undefined variables in function with foreach. + * + * @return void + */ + public function testFunctionWithUndefinedForeach() { + $code = <<<'CODE' + $value1) { + echo "$key1 => $value1\n"; + } + echo "$key1 => $value1\n"; + foreach ($array as $key2 => &$value2) { + echo "$key2 => $value2\n"; + } + echo "$key2 => $value2\n"; + foreach ($array as $element3) { + } + foreach ($array as &$element4) { + } + foreach ($array as $key3 => $value3) { + } + foreach ($array as $key4 => &$value4) { + } +} +CODE; + + $file = $this->dummyFile($code); + + $this->assertWarningsMatch( + $file, + [ + 19 => [ + 24 => ['Unused variable $element3.'] + ], + 21 => [ + 25 => ['Unused variable $element4.'] + ], + 23 => [ + 24 => ['Unused variable $key3.'], + 33 => ['Unused variable $value3.'], + ], + 25 => [ + 24 => ['Unused variable $key4.'], + 34 => ['Unused variable $value4.'] + ], + ] + ); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 14 => ['Variable $array is undefined.'] ], + 7 => [ 14 => ['Variable $array is undefined.'] ], + 11 => [ 14 => ['Variable $array is undefined.'] ], + 15 => [ 14 => ['Variable $array is undefined.'] ], + 19 => [ 14 => ['Variable $array is undefined.'] ], + 21 => [ 14 => ['Variable $array is undefined.'] ], + 23 => [ 14 => ['Variable $array is undefined.'] ], + 25 => [ 14 => ['Variable $array is undefined.'] ], + ] + ); + } + + /** + * Test checks for unused variables in function with foreach, all defined. + * + * @return void + */ + public function testFunctionWithDefinedForeach() { + $code = <<<'CODE' + $value1) { + echo "$key1 => $value1\n"; + } + echo "$key1 => $value1\n"; + foreach ($array as $key2 => &$value2) { + echo "$key2 => $value2\n"; + } + echo "$key2 => $value2\n"; + foreach ($array as $element3) { + } + foreach ($array as &$element4) { + } + foreach ($array as $key3 => $value3) { + } + foreach ($array as $key4 => &$value4) { + } +} +CODE; + + $file = $this->dummyFile($code); + + $this->assertWarningsMatch( + $file, + [ + 20 => [ + 24 => ['Unused variable $element3.'] + ], + 22 => [ + 25 => ['Unused variable $element4.'] + ], + 24 => [ + 24 => ['Unused variable $key3.'], + 33 => ['Unused variable $value3.'] + ], + 26 => [ + 24 => ['Unused variable $key4.'], + 34 => ['Unused variable $value4.'] + ], + ] + ); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks for use of undefined variables in class method without params. + * + * @param string $code Code snippet under test + * @param array $errorsBefore Expected errors before $var is assigned. + * @param array $errorsAfter Optional. Expected errors after $var is + * assigned. Defaults to [] + * + * @dataProvider dataUndefinedVar + * + * @return void + */ + public function testClassNoMembersMethodNoParams($code, $errorsBefore, $errorsAfter = []) { + $template = <<<'CODE' +other_method(); + return $var; + } + + function other_method() { + } +} +CODE; + + list( + $beforeAssign, $afterAssign + ) = $this->insertBeforeAndAfter($template, $code); + + $beforeFile = $this->dummyFile($beforeAssign); + $this->assertWarningsMatch($beforeFile, [], 'before assign'); + $this->assertErrorsMatch($beforeFile, $this->offset($errorsBefore, 3, 8), 'before assign'); + + $afterFile = $this->dummyFile($afterAssign); + $this->assertWarningsMatch($afterFile, [], 'after assign'); + $this->assertErrorsMatch($afterFile, $this->offset($errorsAfter, 5, 8), 'after assign'); + } + + /** + * Test checks for use of undefined variables in class method with param. + * + * @return void + */ + public function testClassNoMembersMethodWithParam() { + $code = <<<'CODE' +other_method(): + return $param; + } + + function other_method() { + } +} +CODE; + + $file = $this->dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks for use of undefined variables in class method using member var. + * + * @return void + */ + public function testClassNoMembersMethodWithMemberVar() { + $code = <<<'CODE' +member_var; + echo self::$static_member_var; + } +} +CODE; + $file = $this->dummyFile($code); + $this->assertWarningsMatch($file, []); + + // NOTE: We don't inspect class inheritance so we can't determine that + // member variables are undeclared. + $this->assertErrorsMatch( + $file, + [ + 4 => [ 21 => ['Variable $member_var is undefined.'] ], + ] + ); + + $this->markTestIncomplete( + 'FIXME: Should we report that self::$static_member_var is undefined?' + ); + $this->assertErrorsMatch( + $file, + [ + 4 => [ 21 => ['Variable $member_var is undefined.'] ], + 5 => [ 21 => ['Variable $static_member_var is undefined.'] ], + ] + ); + } + + /** + * Test checks for use of undefined variables in class with member vars. + */ + public function testClassWithMembersMethodWithMemberVar() { + $code = <<<'CODE' +member_var; + echo $this->no_such_member_var; + echo self::$static_member_var; + echo self::$no_such_static_member_var; + echo SomeOtherClass::$external_static_member_var; + } +} +CODE; + + $file = $this->dummyFile($code); + $this->assertErrorsMatch( + $file, + [ + 8 => [ 21 => ['Variable $no_such_member_var is undefined.'] ], + ] + ); + + $this->markTestIncomplete( + 'FIXME: $static_member_var is actually used and we should add ' . + 'checks for static variable use.' + ); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 8 => [ 21 => ['Variable $no_such_member_var is undefined.'] ], + 10 => [ 21 => ['Variable $no_such_static_member_var is undefined.'] ], + 11 => [ 21 => ['Variable $external_static_member_var is undefined.'] ], + ] + ); + } + + /** + * Test checks for use of $this outside of a class. + */ + public function testThisOutsideClass() { + $code = <<<'CODE' +whatever(); +} +CODE; + + $file = $this->dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 12 => ['Variable $this is undefined.'] ], + ] + ); + } + + /** + * Test checks for use of static members outside of a class. + */ + public function testStaticMembersOutsideClass() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 4 => [ 18 => ['Use of self::$whatever outside class definition.'] ], + ] + ); + } + + /** + * Test checks for use of late static binding outside of a class. + */ + public function testLateStaticBindingOutsideClass() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 18 => ['Use of static::$whatever outside class definition.'] ], + ] + ); + } + + /** + * Test checks for use of variables defined in outer scope of closure. + */ + public function testFunctionWithClosure() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 7 => [ 18 => ['Variable $outer_param is undefined.'] ], + 9 => [ 18 => ['Variable $outer_var is undefined.'] ], + 10 => [ 18 => ['Variable $outer_var2 is undefined.'] ], + 13 => [ 44 => ['Variable $outer_var3 is undefined.'] ], + 16 => [ 18 => ['Variable $inner_param is undefined.'] ], + 18 => [ 18 => ['Variable $outer_var2 is undefined.'] ], + 19 => [ 18 => ['Variable $outer_var3 is undefined.'] ], + 20 => [ 18 => ['Variable $inner_var is undefined.'] ], + 25 => [ 10 => ['Variable $outer_var3 is undefined.'] ], + 26 => [ 10 => ['Variable $inner_param is undefined.'] ], + 27 => [ 10 => ['Variable $inner_var is undefined.'] ], + 28 => [ 10 => ['Variable $inner_var2 is undefined.'] ], + ] + ); + } + + /** + * Test checks function with return by reference. + */ + public function testFunctionWithReturnByRefAndParam() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks function with unused static variables. + */ + public function testFunctionWithStaticVar() { + // phpcs:disable Generic.Files.LineLength.TooLong + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 3 => [ + 40 => ['Unused variable $static_neg_num.'], + 64 => ['Unused variable $static_string.'], + 88 => ['Unused variable $static_string2.'], + 113 => ['Unused variable $static_define.'], + 140 => ['Unused variable $static_constant.'] + ], + ] + ); + $this->assertErrorsMatch( + $file, + [ + 15 => [ 10 => ['Variable $var is undefined.'] ], + ] + ); + } + + /** + * Test checks function with pass by reference param. + */ + public function testFunctionWithPassByRefParam() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks use of undefined vars in function with pass by reference calls. + */ + public function testFunctionWithPassByReferenceCalls() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 10 => ['Variable $matches is undefined.'] ], + 4 => [ 10 => ['Variable $needle is undefined.'] ], + 5 => [ 10 => ['Variable $haystack is undefined.'] ], + 7 => [ 16 => ['Variable $needle is undefined.'] ], + 8 => [ 27 => ['Variable $haystack is undefined.'] ], + 10 => [ 10 => ['Variable $needle is undefined.'] ], + 11 => [ 10 => ['Variable $haystack is undefined.'] ], + 17 => [ 10 => ['Variable $var3 is undefined.'] ], + ] + ); + } + + /** + * Test checks function with try ... catch. + */ + public function testFunctionWithTryCatch() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 10 => ['Variable $e is undefined.'] ], + 7 => [ 14 => ['Variable $e is undefined.'] ], + ] + ); + } + + /** + * Test checks $this usage in a closure within a class. + */ + public function testClassWithThisInsideClosure() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 6 => [ 29 => ['Unused function parameter $inner_param.'] ], + ] + ); + // NOTE: $this is automatically passed to anonymous functions since + // PHP 5.4.0 so no errors for that. + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks self usage in a closure within a class. + */ + public function testClassWithSelfInsideClosure() { + $code = <<<'CODE' +dummyFile($code); + $this->assertErrorsMatch($file, []); + + $this->markTestIncomplete('FIXME: static variable $static_member is used.'); + $this->assertWarningsMatch($file, []); + } + + /** + * Test checks function with inline assignments. + */ + public function testFunctionWithInlineAssigns() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 3 => [ 10 => ['Variable $var is undefined.'] ], + 6 => [ 10 => ['Variable $var2 is undefined.'] ], + ] + ); + } + + /** + * Test checks function with redeclarations as globals. + */ + public function testFunctionWithGlobalRedeclarations() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 7 => [ 20 => ['Redeclaration of bound variable $bound as global variable.'] ], + 14 => [ 12 => ['Redeclaration of function parameter $param as global variable.'] ], + 15 => [ 12 => ['Redeclaration of static variable $static as global variable.'] ], + 16 => [ 12 => ['Redeclaration of variable $bound as global variable.'] ], + 17 => [ 12 => ['Redeclaration of variable $local as global variable.'] ], + 18 => [ 12 => ['Redeclaration of variable $e as global variable.'] ], + ] + ); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks function with redeclarations as static variables. + */ + public function testFunctionWithStaticRedeclarations() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 4 => [ 21 => ['Redeclaration of static variable $static as static variable.'] ], + 7 => [ 19 => ['Redeclaration of bound variable $bound as static variable.'] ], + 14 => [ 12 => ['Redeclaration of function parameter $param as static variable.'] ], + 15 => [ 12 => ['Redeclaration of static variable $static as static variable.'] ], + 16 => [ 12 => ['Redeclaration of variable $bound as static variable.'] ], + 17 => [ 12 => ['Redeclaration of variable $local as static variable.'] ], + 18 => [ 12 => ['Redeclaration of variable $e as static variable.'] ], + ] + ); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks function with redeclarations in catch declarations. + */ + public function testFunctionWithCatchRedeclarations() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks function with superglobals + */ + public function testFunctionWithSuperglobals() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 13 => [ 10 => ['Variable $var is undefined.'] ], + ] + ); + } + + /** + * Test checks function with heredoc + */ + public function testFunctionWithHeredoc() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 8 => [ 1 => ['Variable $var2 is undefined.'] ], + 9 => [ 1 => ['Variable $var2 is undefined.'] ], + 10 => [ 1 => ['Variable $var2 is undefined.'] ], + 12 => [ 1 => ['Variable $var2 is undefined.'] ], + ] + ); + } + + /** + * Test checks variable property names. + */ + public function testClassWithSymbolicRefProperty() { + $code = <<<'CODE' +$property = 'some value'; + $this -> $property = 'some value'; + $this->$undefined_property = 'some value'; + $this -> $undefined_property = 'some value'; + } + } + + function method_with_symbolic_ref_method() { + $methods = array('method_with_symbolic_ref_property'); + foreach ($methods as $method) { + $this->$method(); + $this -> $method(); + $this->$undefined_method(); + $this -> $undefined_method(); + } + } +} +CODE; + + $file = $this->dummyFile($code); + $this->assertErrorsMatch( + $file, + [ + // FIXME: $this->$property refers to defined property. + 8 => [ 20 => ['Variable $property is undefined.'] ], + 9 => [ 22 => ['Variable $property is undefined.'] ], + + 10 => [ 20 => ['Variable $undefined_property is undefined.'] ], + 11 => [ 22 => ['Variable $undefined_property is undefined.'] ], + + 20 => [ 20 => ['Variable $undefined_method is undefined.'] ], + 21 => [ 22 => ['Variable $undefined_method is undefined.'] ], + ] + ); + + $this->markTestIncomplete('FIXME: property $my_property is used + defined.'); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 10 => [ 20 => ['Variable $undefined_property is undefined.'] ], + 11 => [ 22 => ['Variable $undefined_property is undefined.'] ], + + 20 => [ 20 => ['Variable $undefined_method is undefined.'] ], + 21 => [ 22 => ['Variable $undefined_method is undefined.'] ], + ] + ); + } + + /** + * Test checks function with only assign to pass by reference parameter. + */ + public function testFunctionWithPassByRefAssignOnlyArg() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch($file, []); + } + + /** + * Test checks class with late static binding. + */ + public function testClassWithLateStaticBinding() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch($file, []); + $this->assertErrorsMatch( + $file, + [ + 5 => [ 29 => ['Variable $var is undefined.'] ], + ] + ); + } + + /** + * Test checks function with single quoted compact. + */ + public function testFunctionWithSingleQuotedCompact() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 2 => [ 49 => ['Unused function parameter $param2.'] ], + ] + ); + $this->assertErrorsMatch( + $file, + [ + 7 => [ 23 => ['Variable $var3 is undefined.'] ], + 10 => [ 54 => ['Variable $var5 is undefined.'] ], + ] + ); + } + + /** + * Test checks function with double quoted expression compact. + */ + public function testFunctionWithExpressionCompact() { + $code = <<<'CODE' +dummyFile($code); + $this->assertWarningsMatch( + $file, + [ + 2 => [ 52 => ['Unused function parameter $param2.'] ], + 7 => [ 5 => ['Unused variable $var7.'] ], + ] + ); + $this->assertErrorsMatch( + $file, + [ + 11 => [ 23 => ['Variable $var3 is undefined.'] ], + 14 => [ 54 => ['Variable $var5 is undefined.'] ], + ] + ); } } diff --git a/phpunit.xml b/phpunit.xml index 34ad71c..12b70d1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,7 +2,7 @@ - vendor/squizlabs/php_codesniffer/tests/Standards/AllSniffs.php + VariableAnalysis/Tests/VariableAnalysis From 084dc014072d727da8540b01d0aa34ea605e94fd Mon Sep 17 00:00:00 2001 From: Bill Ruddock Date: Mon, 8 Nov 2021 05:11:29 +0000 Subject: [PATCH 3/4] Ignore phpunit result cache file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4b8618d..6d5bb10 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ util/ mybuild mytest myrun +.phpunit.result.cache From f3db7f8a27c16e87ca9296ca23e1291101d31661 Mon Sep 17 00:00:00 2001 From: Bill Ruddock Date: Mon, 8 Nov 2021 05:19:39 +0000 Subject: [PATCH 4/4] Use colours in phpunit output --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 12b70d1..016a868 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + VariableAnalysis/Tests/VariableAnalysis