From d385e14d1d438c62faa24033c78d4c39d4f4200c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sat, 7 Oct 2017 10:28:52 +0200 Subject: [PATCH 1/5] PoC --- .TODO | 17 + TODO-roave | 112 ++++ composer.json | 7 +- composer.lock | 499 +++++++++++------- fixtures/set002/scoped/scoper.inc.php | 2 + phpunit.xml.dist | 2 +- specs/binary/php-parser.php | 406 -------------- specs/binary/phpunit.php | 113 ---- specs/binary/simple.php | 111 ++++ specs/class-FQ.php | 441 +++++++--------- ...h-single-level-use-statement-and-alias.php | 51 +- ...-level-with-single-level-use-statement.php | 42 +- .../class-const/global-scope-single-level.php | 30 +- ...-level-with-single-level-use-and-alias.php | 134 ++++- ...-scope-two-level-with-single-level-use.php | 146 ++++- specs/class-const/global-scope-two-level.php | 66 ++- ...h-single-level-use-statement-and-alias.php | 75 ++- .../namespace-scope-single-level.php | 27 +- ...-level-with-single-level-use-and-alias.php | 102 +++- ...-scope-two-level-with-single-level-use.php | 138 ++++- .../class-const/namespace-scope-two-level.php | 58 +- ...e-part-with-single-level-use-statement.php | 60 ++- specs/class/abstract.php | 79 ++- specs/class/anonymous.php | 109 ++-- specs/class/conditional.php | 38 +- specs/class/final.php | 6 +- specs/class/interface.php | 84 ++- specs/class/regular-extend.php | 17 +- specs/class/regular.php | 118 +++-- specs/class/trait.php | 6 +- ...h-single-level-use-statement-and-alias.php | 4 + ...global-with-single-level-use-statement.php | 4 + specs/const/global-scope-global.php | 4 + ...namespaced-with-single-level-use-alias.php | 80 ++- ...-part-namespaced-with-single-level-use.php | 61 ++- .../global-scope-single-part-namespaced.php | 6 + ...spaced-with-single-level-use-and-alias.php | 27 +- ...parts-namespaced-with-single-level-use.php | 27 +- .../global-scope-two-parts-namespaced.php | 6 + specs/func-args/func.php | 29 +- specs/func-args/misc.php | 2 + specs/func-args/whitelisted-func.php | 51 +- specs/func-declaration/global.php | 300 +++++++++-- specs/func-declaration/namespace.php | 344 +++++++++++- ...h-single-level-use-statement-and-alias.php | 4 + ...l-func-with-single-level-use-statement.php | 4 + specs/function/global-scope-global-func.php | 4 + ...d-func-with-single-level-use-and-alias.php | 86 ++- ...-namespaced-func-with-single-level-use.php | 46 +- ...obal-scope-single-part-namespaced-func.php | 8 + specs/misc.php | 24 +- specs/namespace/braces.php | 163 +----- specs/namespace/creation-for-whitelist.php | 143 ++--- specs/namespace/no-braces.php | 163 +++++- ...th-single-level-use-statement-an-alias.php | 195 +++++++ ...h-single-level-use-statement-and-alias.php | 113 ---- ...e-part-with-single-level-use-statement.php | 84 +-- specs/new/global-scope-single-part.php | 61 +-- ...-parts-with-single-level-use-and-alias.php | 228 +++++--- ...-scope-two-parts-with-single-level-use.php | 285 ++++++++-- specs/new/global-scope-two-parts.php | 81 ++- ...e-part-with-single-level-use-statement.php | 96 ++-- specs/new/namespace-single-part.php | 42 +- ...espace-two-parts-with-single-level-use.php | 97 ++-- ...namespace-two-parts-with-two-level-use.php | 103 +++- specs/new/namespace-two-parts.php | 78 +-- .../self-static-parent-const.php | 65 +-- .../self-static-parent-method.php | 95 ++-- .../self-static-parent-static-var.php | 65 +-- ...h-single-level-use-statement-and-alias.php | 51 +- ...e-part-with-single-level-use-statement.php | 42 +- .../global-scope-single-part.php | 41 +- ...-parts-with-single-level-use-and-alias.php | 207 +++++++- ...-scope-two-parts-with-single-level-use.php | 206 +++++++- .../static-method/global-scope-two-parts.php | 67 ++- ...e-part-with-single-level-use-statement.php | 60 ++- specs/static-method/namespace-single-part.php | 24 +- ...espace-two-parts-with-single-level-use.php | 60 ++- ...namespace-two-parts-with-two-level-use.php | 103 +++- specs/static-method/namespace-two-parts.php | 55 +- specs/use/use-class-alias.php | 72 +-- specs/use/use-class-group.php | 6 + specs/use/use-class.php | 122 +++-- specs/use/use-const-alias.php | 10 + specs/use/use-const-group.php | 2 + specs/use/use-const.php | 34 +- specs/use/use-func-alias.php | 10 + specs/use/use-func-group.php | 2 + specs/use/use-func.php | 10 + specs/use/use-mix-group.php | 2 + src/Console/Command/AddPrefixCommand.php | 7 +- src/Console/Command/SelfUpdateCommand.php | 4 +- src/Console/Configuration.php | 80 +-- src/NodeTraverser.php | 163 ++++++ src/NodeVisitor/NameStmtPrefixer.php | 26 +- src/NodeVisitor/NamespaceStmtPrefixer.php | 64 ++- src/NodeVisitor/StringScalarPrefixer.php | 22 +- .../UseStmt/GroupUseStmtTransformer.php | 87 --- src/NodeVisitor/UseStmt/UseStmtPrefixer.php | 17 +- src/Scoper.php | 11 +- .../Composer/InstalledPackagesScoper.php | 4 +- src/Scoper/Composer/JsonFileScoper.php | 4 +- src/Scoper/NullScoper.php | 2 +- src/Scoper/PatchScoper.php | 4 +- src/Scoper/PhpScoper.php | 6 +- src/Scoper/TraverserFactory.php | 31 +- src/functions.php | 5 +- src/scoper.inc.php.tpl | 13 - .../Console/Command/AddPrefixCommandTest.php | 39 +- .../Composer/InstalledPackagesScoperTest.php | 9 +- tests/Scoper/Composer/JsonFileScoperTest.php | 12 +- tests/Scoper/FakeScoper.php | 2 +- tests/Scoper/NullScoperTest.php | 4 +- tests/Scoper/PatchScoperTest.php | 7 +- tests/Scoper/PhpScoperTest.php | 119 ++++- tests/Scoper/TraverserFactoryTest.php | 11 +- tests/functions.php | 7 - 117 files changed, 5432 insertions(+), 2888 deletions(-) create mode 100644 .TODO create mode 100644 TODO-roave delete mode 100644 specs/binary/php-parser.php delete mode 100644 specs/binary/phpunit.php create mode 100644 specs/binary/simple.php create mode 100644 specs/new/global-scope-single-part-with-single-level-use-statement-an-alias.php delete mode 100644 specs/new/global-scope-single-part-with-single-level-use-statement-and-alias.php create mode 100644 src/NodeTraverser.php delete mode 100644 src/NodeVisitor/UseStmt/GroupUseStmtTransformer.php diff --git a/.TODO b/.TODO new file mode 100644 index 00000000..dedd9e8b --- /dev/null +++ b/.TODO @@ -0,0 +1,17 @@ +Right now whitelisted class works as follow: + +- They are prefixed but a class_alias statement is appended to their declaration with the old name +- Whitelisted class are dumped in a `scoper-autoload.php` autoloader which the users can use. This way, the class + aliases will be triggered at that time + +Right now the use statements of whitelisted class are not prefixed. IMO this should not matter and we could prefix +them still. This would reduce the need of custom code to treat them differently. + + + + +----- + + +Prefix the return types of functions & methods; see func-declaration/global.php +Prefix functions if they are not internal diff --git a/TODO-roave b/TODO-roave new file mode 100644 index 00000000..9d2324b6 --- /dev/null +++ b/TODO-roave @@ -0,0 +1,112 @@ +// vendor/roave/better-reflection/src/SourceLocator/Ast/Locator.php + +findReflectionsInTree = new FindReflectionsInTree(new NodeToReflection()); + + $this->parser = $parser; + } + + /** + * @param Reflector $reflector + * @param LocatedSource $locatedSource + * @param Identifier $identifier + * @return Reflection + * @throws \Roave\BetterReflection\Reflector\Exception\IdentifierNotFound + * @throws Exception\ParseToAstFailure + */ + public function findReflection( + Reflector $reflector, + LocatedSource $locatedSource, + Identifier $identifier + ) : Reflection { + return $this->findInArray( + $this->findReflectionsOfType( + $reflector, + $locatedSource, + $identifier->getType() + ), + $identifier + ); + } + + /** + * Get an array of reflections found in some code. + * + * @param Reflector $reflector + * @param LocatedSource $locatedSource + * @param IdentifierType $identifierType + * @return \Roave\BetterReflection\Reflection\Reflection[] + * @throws Exception\ParseToAstFailure + */ + public function findReflectionsOfType( + Reflector $reflector, + LocatedSource $locatedSource, + IdentifierType $identifierType + ) : array { + try { + return $this->findReflectionsInTree->__invoke( + $reflector, + $this->parser->parse($locatedSource->getSource()), + $identifierType, + $locatedSource + ); + } catch (Throwable $exception) { + throw Exception\ParseToAstFailure::fromLocatedSource($locatedSource, $exception); + } + } + + /** + * Given an array of Reflections, try to find the identifier. + * + * @param Reflection[] $reflections + * @param Identifier $identifier + * @return Reflection + * @throws \Roave\BetterReflection\Reflector\Exception\IdentifierNotFound + */ + private function findInArray(array $reflections, Identifier $identifier) : Reflection + { + // MODIF HERE + $identifierName = strtolower($identifier->getName()); + + foreach ($reflections as $reflection) { + if (strtolower($reflection->getName()) === $identifierName) { + // -MODIF HERE + return $reflection; + } + } + + throw IdentifierNotFound::fromIdentifier($identifier); + } +} diff --git a/composer.json b/composer.json index 254a5612..9413f749 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,10 @@ "nikic/php-parser": "^3.0", "ocramius/package-versions": "^1.1", "padraic/phar-updater": "^1.0", - "symfony/console": "^3.2|^4.0", - "symfony/filesystem": "^3.2|^4.0", - "symfony/finder": "^3.2|^4.0", + "roave/better-reflection": "^2.0", + "symfony/console": "^3.2 || ^4.0", + "symfony/filesystem": "^3.2 || ^4.0", + "symfony/finder": "^3.2 || ^4.0", "symfony/requirements-checker": "^1.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index f8f58740..cde82302 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "2d553b942937decb14a15894ab741c36", + "content-hash": "f767378019317e3bacbde6bf9051f716", "packages": [ { "name": "composer/semver", @@ -277,6 +277,152 @@ ], "time": "2017-07-12T22:42:45+00:00" }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-08-30T18:51:59+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, { "name": "psr/log", "version": "1.0.2", @@ -324,6 +470,101 @@ ], "time": "2016-10-10T12:19:37+00:00" }, + { + "name": "roave/better-reflection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/Roave/BetterReflection.git", + "reference": "619be9a2cef239245d8f1f466bcddc037949b788" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/BetterReflection/zipball/619be9a2cef239245d8f1f466bcddc037949b788", + "reference": "619be9a2cef239245d8f1f466bcddc037949b788", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^3.1.1", + "php": ">7.1.0,<7.3.0", + "phpdocumentor/reflection-docblock": "^4.1.1", + "phpdocumentor/type-resolver": "^0.4.0", + "roave/signature": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.3.0" + }, + "suggest": { + "composer/composer": "Required to use the ComposerSourceLocator" + }, + "type": "library", + "autoload": { + "psr-4": { + "Roave\\BetterReflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.io/" + }, + { + "name": "James Titcumb", + "email": "james@asgrim.com", + "homepage": "https://github.com/asgrim" + }, + { + "name": "Gary Hockin", + "email": "gary@roave.com", + "homepage": "https://github.com/geeh" + }, + { + "name": "Jaroslav Hanslík", + "email": "kukulich@kukulich.cz", + "homepage": "https://github.com/kukulich" + } + ], + "description": "Better Reflection - an improved code reflection API", + "time": "2017-09-16T20:30:16+00:00" + }, + { + "name": "roave/signature", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/Roave/Signature.git", + "reference": "bed4ecbdd7f312ab6bb39561ac191f520bcee386" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/Signature/zipball/bed4ecbdd7f312ab6bb39561ac191f520bcee386", + "reference": "bed4ecbdd7f312ab6bb39561ac191f520bcee386", + "shasum": "" + }, + "require": { + "php": "^7.0|^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Roave\\Signature\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Sign and verify stuff", + "time": "2017-02-17T13:53:21+00:00" + }, { "name": "symfony/console", "version": "v3.3.10", @@ -652,6 +893,56 @@ "distribution" ], "time": "2017-10-01T16:30:53+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23T20:04:58+00:00" } ], "packages-dev": [ @@ -892,152 +1183,6 @@ "description": "Library for handling version information and constraints", "time": "2017-03-05T17:38:23+00:00" }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.1.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-30T18:51:59+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, { "name": "phpspec/prophecy", "version": "v1.7.2", @@ -1353,16 +1498,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.4.0", + "version": "6.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1bcaca096998de32c29535fdd2dea0c475e8f61" + "reference": "b770d8ba7e60295ee91d69d5a5e01ae833cac220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1bcaca096998de32c29535fdd2dea0c475e8f61", - "reference": "a1bcaca096998de32c29535fdd2dea0c475e8f61", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b770d8ba7e60295ee91d69d5a5e01ae833cac220", + "reference": "b770d8ba7e60295ee91d69d5a5e01ae833cac220", "shasum": "" }, "require": { @@ -1433,7 +1578,7 @@ "testing", "xunit" ], - "time": "2017-10-06T03:14:57+00:00" + "time": "2017-10-07T17:53:53+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -2092,56 +2237,6 @@ ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2016-11-23T20:04:58+00:00" } ], "aliases": [], diff --git a/fixtures/set002/scoped/scoper.inc.php b/fixtures/set002/scoped/scoper.inc.php index 0b67a5fe..63472aaa 100644 --- a/fixtures/set002/scoped/scoper.inc.php +++ b/fixtures/set002/scoped/scoper.inc.php @@ -1,3 +1,5 @@ diff --git a/specs/binary/php-parser.php b/specs/binary/php-parser.php deleted file mode 100644 index 68096f99..00000000 --- a/specs/binary/php-parser.php +++ /dev/null @@ -1,406 +0,0 @@ -, - * Pádraic Brady - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'meta' => [ - 'title' => 'PHP-Parser binary file', - // Default values. If not specified will be the one used - 'prefix' => 'Humbug', - 'whitelist' => [], - ], - - '' => <<<'PHP' -#!/usr/bin/env php - array( - 'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments' -))); -$parser = (new PhpParser\ParserFactory)->create( - PhpParser\ParserFactory::PREFER_PHP7, - $lexer -); -$dumper = new PhpParser\NodeDumper([ - 'dumpComments' => true, - 'dumpPositions' => $attributes['with-positions'], -]); -$prettyPrinter = new PhpParser\PrettyPrinter\Standard; -$serializer = new PhpParser\Serializer\XML; - -$traverser = new PhpParser\NodeTraverser(); -$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); - -foreach ($files as $file) { - if (strpos($file, ' Code $code\n"; - } else { - if (!file_exists($file)) { - die("File $file does not exist.\n"); - } - - $code = file_get_contents($file); - echo "====> File $file:\n"; - } - - if ($attributes['with-recovery']) { - $errorHandler = new PhpParser\ErrorHandler\Collecting; - $stmts = $parser->parse($code, $errorHandler); - foreach ($errorHandler->getErrors() as $error) { - $message = formatErrorMessage($error, $code, $attributes['with-column-info']); - echo $message . "\n"; - } - if (null === $stmts) { - continue; - } - } else { - try { - $stmts = $parser->parse($code); - } catch (PhpParser\Error $error) { - $message = formatErrorMessage($error, $code, $attributes['with-column-info']); - die($message . "\n"); - } - } - - foreach ($operations as $operation) { - if ('dump' === $operation) { - echo "==> Node dump:\n"; - echo $dumper->dump($stmts, $code), "\n"; - } elseif ('pretty-print' === $operation) { - echo "==> Pretty print:\n"; - echo $prettyPrinter->prettyPrintFile($stmts), "\n"; - } elseif ('serialize-xml' === $operation) { - echo "==> Serialized XML:\n"; - echo $serializer->serialize($stmts), "\n"; - } elseif ('var-dump' === $operation) { - echo "==> var_dump():\n"; - var_dump($stmts); - } elseif ('resolve-names' === $operation) { - echo "==> Resolved names.\n"; - $stmts = $traverser->traverse($stmts); - } - } -} - -function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) { - if ($withColumnInfo && $e->hasColumnInfo()) { - return $e->getMessageWithColumnInfo($code); - } else { - return $e->getMessage(); - } -} - -function showHelp($error = '') { - if ($error) { - echo $error . "\n\n"; - } - die(<< false, - 'with-positions' => false, - 'with-recovery' => false, - ); - - array_shift($args); - $parseOptions = true; - foreach ($args as $arg) { - if (!$parseOptions) { - $files[] = $arg; - continue; - } - - switch ($arg) { - case '--dump': - case '-d': - $operations[] = 'dump'; - break; - case '--pretty-print': - case '-p': - $operations[] = 'pretty-print'; - break; - case '--serialize-xml': - $operations[] = 'serialize-xml'; - break; - case '--var-dump': - $operations[] = 'var-dump'; - break; - case '--resolve-names': - case '-N'; - $operations[] = 'resolve-names'; - break; - case '--with-column-info': - case '-c'; - $attributes['with-column-info'] = true; - break; - case '--with-positions': - case '-P': - $attributes['with-positions'] = true; - break; - case '--with-recovery': - case '-r': - $attributes['with-recovery'] = true; - break; - case '--help': - case '-h'; - showHelp(); - break; - case '--': - $parseOptions = false; - break; - default: - if ($arg[0] === '-') { - showHelp("Invalid operation $arg."); - } else { - $files[] = $arg; - } - } - } - - return array($operations, $files, $attributes); -} - ----- -#!/usr/bin/env php - array('startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'))); -$parser = (new \Humbug\PhpParser\ParserFactory())->create(\Humbug\PhpParser\ParserFactory::PREFER_PHP7, $lexer); -$dumper = new \Humbug\PhpParser\NodeDumper(['dumpComments' => \true, 'dumpPositions' => $attributes['with-positions']]); -$prettyPrinter = new \Humbug\PhpParser\PrettyPrinter\Standard(); -$serializer = new \Humbug\PhpParser\Serializer\XML(); -$traverser = new \Humbug\PhpParser\NodeTraverser(); -$traverser->addVisitor(new \Humbug\PhpParser\NodeVisitor\NameResolver()); -foreach ($files as $file) { - if (\strpos($file, ' Code {$code}\n"; - } else { - if (!\file_exists($file)) { - die("File {$file} does not exist.\n"); - } - $code = \file_get_contents($file); - echo "====> File {$file}:\n"; - } - if ($attributes['with-recovery']) { - $errorHandler = new \Humbug\PhpParser\ErrorHandler\Collecting(); - $stmts = $parser->parse($code, $errorHandler); - foreach ($errorHandler->getErrors() as $error) { - $message = \formatErrorMessage($error, $code, $attributes['with-column-info']); - echo $message . "\n"; - } - if (\null === $stmts) { - continue; - } - } else { - try { - $stmts = $parser->parse($code); - } catch (PhpParser\Error $error) { - $message = \formatErrorMessage($error, $code, $attributes['with-column-info']); - die($message . "\n"); - } - } - foreach ($operations as $operation) { - if ('dump' === $operation) { - echo "==> Node dump:\n"; - echo $dumper->dump($stmts, $code), "\n"; - } elseif ('pretty-print' === $operation) { - echo "==> Pretty print:\n"; - echo $prettyPrinter->prettyPrintFile($stmts), "\n"; - } elseif ('serialize-xml' === $operation) { - echo "==> Serialized XML:\n"; - echo $serializer->serialize($stmts), "\n"; - } elseif ('var-dump' === $operation) { - echo "==> var_dump():\n"; - \var_dump($stmts); - } elseif ('resolve-names' === $operation) { - echo "==> Resolved names.\n"; - $stmts = $traverser->traverse($stmts); - } - } -} -function formatErrorMessage(\Humbug\PhpParser\Error $e, $code, $withColumnInfo) -{ - if ($withColumnInfo && $e->hasColumnInfo()) { - return $e->getMessageWithColumnInfo($code); - } else { - return $e->getMessage(); - } -} -function showHelp($error = '') -{ - if ($error) { - echo $error . "\n\n"; - } - die(<< \false, 'with-positions' => \false, 'with-recovery' => \false); - \array_shift($args); - $parseOptions = \true; - foreach ($args as $arg) { - if (!$parseOptions) { - $files[] = $arg; - continue; - } - switch ($arg) { - case '--dump': - case '-d': - $operations[] = 'dump'; - break; - case '--pretty-print': - case '-p': - $operations[] = 'pretty-print'; - break; - case '--serialize-xml': - $operations[] = 'serialize-xml'; - break; - case '--var-dump': - $operations[] = 'var-dump'; - break; - case '--resolve-names': - case '-N': - $operations[] = 'resolve-names'; - break; - case '--with-column-info': - case '-c': - $attributes['with-column-info'] = \true; - break; - case '--with-positions': - case '-P': - $attributes['with-positions'] = \true; - break; - case '--with-recovery': - case '-r': - $attributes['with-recovery'] = \true; - break; - case '--help': - case '-h': - \showHelp(); - break; - case '--': - $parseOptions = \false; - break; - default: - if ($arg[0] === '-') { - \showHelp("Invalid operation {$arg}."); - } else { - $files[] = $arg; - } - } - } - return array($operations, $files, $attributes); -} - -PHP - , -]; diff --git a/specs/binary/phpunit.php b/specs/binary/phpunit.php deleted file mode 100644 index 6593e6cb..00000000 --- a/specs/binary/phpunit.php +++ /dev/null @@ -1,113 +0,0 @@ -, - * Pádraic Brady - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'meta' => [ - 'title' => 'PHPUnit binary file', - // Default values. If not specified will be the one used - 'prefix' => 'Humbug', - 'whitelist' => [], - ], - - '' => <<<'PHP' -#!/usr/bin/env php - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -if (version_compare('7.0.0', PHP_VERSION, '>')) { - fwrite( - STDERR, - sprintf( - 'This version of PHPUnit is supported on PHP 7.0 and PHP 7.1.' . PHP_EOL . - 'You are using PHP %s (%s).' . PHP_EOL, - PHP_VERSION, - PHP_BINARY - ) - ); - - die(1); -} - -if (!ini_get('date.timezone')) { - ini_set('date.timezone', 'UTC'); -} - -foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) { - if (file_exists($file)) { - define('PHPUNIT_COMPOSER_INSTALL', $file); - - break; - } -} - -unset($file); - -if (!defined('PHPUNIT_COMPOSER_INSTALL')) { - fwrite( - STDERR, - 'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL . - ' composer install' . PHP_EOL . PHP_EOL . - 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL - ); - - die(1); -} - -require PHPUNIT_COMPOSER_INSTALL; - -PHPUnit\TextUI\Command::main(); - ----- -#!/usr/bin/env php - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -if (\version_compare('7.0.0', \PHP_VERSION, '>')) { - \fwrite(\STDERR, \sprintf('This version of PHPUnit is supported on PHP 7.0 and PHP 7.1.' . \PHP_EOL . 'You are using PHP %s (%s).' . \PHP_EOL, \PHP_VERSION, \PHP_BINARY)); - die(1); -} -if (!\ini_get('date.timezone')) { - \ini_set('date.timezone', 'UTC'); -} -foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) { - if (\file_exists($file)) { - \define('PHPUNIT_COMPOSER_INSTALL', $file); - break; - } -} -unset($file); -if (!\defined('PHPUNIT_COMPOSER_INSTALL')) { - \fwrite(\STDERR, 'You need to set up the project dependencies using Composer:' . \PHP_EOL . \PHP_EOL . ' composer install' . \PHP_EOL . \PHP_EOL . 'You can learn all about Composer on https://getcomposer.org/.' . \PHP_EOL); - die(1); -} -require \PHPUNIT_COMPOSER_INSTALL; -\Humbug\PHPUnit\TextUI\Command::main(); - -PHP - , -]; diff --git a/specs/binary/simple.php b/specs/binary/simple.php new file mode 100644 index 00000000..3729445c --- /dev/null +++ b/specs/binary/simple.php @@ -0,0 +1,111 @@ +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'meta' => [ + 'title' => 'Simple binary file', + // Default values. If not specified will be the one used + 'prefix' => 'Humbug', + 'whitelist' => [], + ], + + 'some statements made directly in the global namespace: wrap them in a namespace statement' => <<<'PHP' + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\true) { + echo "yo"; +} + +if (\false) { + echo "oy"; +} + +---- + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (\true) { + echo "yo"; +} +if (\false) { + echo "oy"; +} + +PHP + , + + 'some statements made directly in the global namespace with a shebang: wrap them in a namespace statement' => <<<'PHP' +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\true) { + echo "yo"; +} + +if (\false) { + echo "oy"; +} + +---- +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (\true) { + echo "yo"; +} +if (\false) { + echo "oy"; +} + +PHP + , +]; diff --git a/specs/class-FQ.php b/specs/class-FQ.php index 76e32a4c..7e2eecb8 100644 --- a/specs/class-FQ.php +++ b/specs/class-FQ.php @@ -23,7 +23,7 @@ [ 'spec' => <<<'SPEC' Different kind of whitelisted class constant calls in the global scope: -- do not prefix the use classes: they are all whitelisted +- prefix the whitelisted classes and append their class aliases - transforms the call into a FQ - resolve the aliases SPEC @@ -32,74 +32,66 @@ 'payload' => <<<'PHP' <<<'SPEC' -Different kind of class constant calls in the global scope: -- do not prefix the use classes: they are all whitelisted -- transforms the call into a FQ -- resolve the aliases -SPEC - , - 'payload' => <<<'PHP' +namespace { + use Foo as X; + use Foo\Bar as Y; + use Foo\Bar\Poz as Z; + + Foo::MAIN_CONST; + X::MAIN_CONST; + + Y::MAIN_CONST; + X\Bar::MAIN_CONST; + Foo\Bar::MAIN_CONST; + + Z::MAIN_CONST; + Y\Poz::MAIN_CONST; + X\Bar\Poz::MAIN_CONST; + Foo\Bar\Poz::MAIN_CONST; +} + +---- <<<'SPEC' -Different kind of whitelisted class constant calls in a namespace: -- do not prefix the use classes: they are all whitelisted +Different kind of class constant calls in the global scope: +- prefix the whitelisted classes and append their class aliases - transforms the call into a FQ - resolve the aliases SPEC , - 'whitelist' => [ - 'Foo\Bar', - 'Foo\Bar\Poz', - - 'A\Foo', - 'A\Foo\Bar', - 'A\Foo\Bar\Poz', - 'A\Aoo', - 'A\Aoo\Aoz', - 'A\Aoz', - 'A\Aoo\Aoz\Poz', - ], 'payload' => <<<'PHP' <<<'SPEC' -Different kind of class constant calls in a namespace: -- do not prefix the use classes: they are all whitelisted -- transforms the call into a FQ -- resolve the aliases -SPEC - , - 'payload' => <<<'PHP' +namespace { + use Foo as X; + use Foo\Bar as Y; + use Foo\Bar\Poz as Z; + + Foo::MAIN_CONST; + X::MAIN_CONST; + + Y::MAIN_CONST; + X\Bar::MAIN_CONST; + Foo\Bar::MAIN_CONST; + + Z::MAIN_CONST; + Y\Poz::MAIN_CONST; + X\Bar\Poz::MAIN_CONST; + Foo\Bar\Poz::MAIN_CONST; +} +---- <<<'SPEC' -Different kind of whitelisted class constant calls in multiple namespaces: -- do not prefix the use classes: they are all whitelisted +Different kind of class constant calls in a namespace: +- prefix the whitelisted classes and append their class aliases - transforms the call into a FQ - resolve the aliases SPEC @@ -254,59 +201,51 @@ 'A\Aoo\Aoz', 'A\Aoz', 'A\Aoo\Aoz\Poz', - - 'B\Foo', - 'B\Foo\Bar', - 'B\Foo\Bar\Poz', - 'B\Aoo', - 'B\Aoo\Aoz', - 'B\Aoz', - 'B\Aoo\Aoz\Poz', ], 'payload' => <<<'PHP' <<<'SPEC' Constant call on a aliased class which is imported via an aliased use statement and which belongs to the global namespace: -- do not prefix the use statement (cf. class belonging to the global scope tests) -- transform the call into a FQ call +- prefix the use statement (cf. class belonging to the global scope tests) +- prefix the constant SPEC , 'payload' => <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: add prefixed namespace.' => <<<'PHP' [ + 'Declaration of a whitelisted class: append aliasing.' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' <<<'PHP' + [ + 'spec' => <<<'SPEC' +Multiple declarations in different namespaces with whitelisted classes: prefix each namespace. +SPEC + , + 'whitelist' => ['Foo\WA', 'Bar\WB', 'WC'], + 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: prefix non-internal classes.' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: warp in a prefixed namespace.' => <<<'PHP' <<<'SPEC' +Declaration of a whitelisted class in the global namespace: warp in a prefixed namespace. + +TODO: unsupported at the moment. The `class_alias` statement appended to support whitelisted classes are added at the +end of a namespace statement for now. This could be supported if they are added right after the declaration statement +instead. +SPEC + , + 'whitelist' => ['A'], + 'payload' => <<<'PHP' + [ + 'Declaration of a whitelisted class: prefix the namespace, too dynamic to account for.' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: add prefixed namespace.' => <<<'PHP' [ + 'Declaration of a whitelisted final class: append aliasing.' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: add a prefixed namespace.' => <<<'PHP' [ + 'Declaration of a whitelisted interface: append aliasing.' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: prefix only non-internal classes.' => <<<'PHP' [ + 'Declaration of a whitelisted class: append aliasing.' => [ 'whitelist' => ['Foo\B'], 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: add a prefixed namespace.' => <<<'PHP' [ + 'Declaration of a whitelisted class: append aliasing.' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' [], ], - 'Declaration in the global namespace: do not do anything.' => <<<'PHP' + 'Declaration in the global namespace: add prefixed namespace.' => <<<'PHP' [ + 'Declaration of a whitelisted trait: prefix the namespace (whitelists works only on classes).' => [ 'whitelist' => ['Foo\A'], 'payload' => <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' Function declaration in the global namespace: -- prefix each argument +- prefix the namespace statements +- prefix the appropriate classes SPEC , 'whitelist' => ['X\Y'], 'payload' => <<<'PHP' <<<'SPEC' Function declaration in the global namespace with use statements: -- prefix the use appropriate statements -- prefix each argument -- do not prefix whitelisted classes +- prefix namespace statements +- prefix the appropriate classes +- append the class_alias statement to the whitelisted class SPEC , 'whitelist' => ['X\Y'], 'payload' => <<<'PHP' <<<'SPEC' +Function declarations with return types in the global namespace with use statements: +- prefix namespace statements +- prefix the appropriate classes +- append the class_alias statement to the whitelisted class +SPEC + , + 'whitelist' => ['X\Y'], + 'payload' => <<<'PHP' + <<<'SPEC' Function declaration in a namespace: -- prefix the namespace -- prefix each argument -- do not prefix whitelisted classes +- prefix the namespace statements +- prefix the appropriate classes SPEC , 'whitelist' => ['X\Y'], 'payload' => <<<'PHP' <<<'SPEC' +Function declaration in a namespace with use statements: +- prefix namespace statements +- prefix the appropriate classes +- append the class_alias statement to the whitelisted class +SPEC + , + 'whitelist' => ['X\Y'], + 'payload' => <<<'PHP' + <<<'SPEC' +Function declarations with return types in a namespace with use statements: +- prefix namespace statements +- prefix the appropriate classes +- append the class_alias statement to the whitelisted class +SPEC + , + 'whitelist' => ['X\Y'], + 'payload' => <<<'PHP' + <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' -When resolving fully qualified class names, keep in mind that classes are case insensitive in PHP. +Account for PHP case insentitiveness when resolving FQCNs. SPEC , 'payload' => <<<'PHP' @@ -52,6 +52,7 @@ namespace Foo { class X {} + class StdClasS {} } namespace { @@ -64,17 +65,20 @@ class X {} ---- [ - 'title' => 'Namespace declarations without braces', + 'title' => 'Namespace declarations with braces', // Default values. If not specified will be the one used 'prefix' => 'Humbug', 'whitelist' => [], ], - 'Root namespace: do nothing' => <<<'PHP' + 'One level namespace: prefix it' => <<<'PHP' <<<'PHP' - [ - 'whitelist' => ['Foo'], - 'payload' => <<<'PHP' - <<<'PHP' - <<<'PHP' [ - 'whitelist' => ['Foo'], - 'payload' => <<<'PHP' - <<<'PHP' - <<<'PHP' - <<<'PHP' + 'Defines should be wrapped in namespace alongside rest.' => <<<'PHP' <<<'PHP' + 'Make sure anonymous classes are wrapped in a prefixed namespace.' => <<<'PHP' <<<'PHP' - <<<'PHP' + 'Make sure traits are wrapped in a prefix namespace.' => <<<'PHP' [ - 'title' => 'Namespace declarations with braces', + 'title' => 'Namespace declarations without braces', // Default values. If not specified will be the one used 'prefix' => 'Humbug', 'whitelist' => [], ], - 'One level namespace: prefix it' => <<<'PHP' + 'Root namespace: prefix the namespace' => <<<'PHP' <<<'PHP' + [ + 'whitelist' => ['Foo'], + 'payload' => <<<'PHP' + <<<'PHP' + <<<'PHP' [ + 'whitelist' => ['Foo'], + 'payload' => <<<'PHP' + <<<'PHP' + <<<'PHP' +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'meta' => [ + 'title' => 'New statement call of a class imported via an aliased use statement in the global scope', + // Default values. If not specified will be the one used + 'prefix' => 'Humbug', + 'whitelist' => [], + ], + + [ + 'spec' => <<<'SPEC' +New statement call of a class belonging to the global namespace imported via an aliased use statement: +- prefix the namespace, use and new statements +- transform the call into a FQ call +SPEC + , + 'payload' => <<<'PHP' + <<<'SPEC' +New statement call of a class belonging to the global namespace imported via an aliased use statement; the call is made +using the original class instead of the alias +- prefix the namespace, use and new statements +- transform the call into a FQ call +SPEC + , + 'payload' => <<<'PHP' + <<<'SPEC' +FQ new statement call of a class belonging to the global namespace imported via an aliased use statement: +- prefix the namespace, use and new statements +SPEC + , + 'payload' => <<<'PHP' + <<<'SPEC' +FQ new statement call of a class belonging to the global namespace imported via an aliased use statement; the new +statement uses the class directly instead of the alias +- prefix the namespace, use and new statements +SPEC + , + 'payload' => <<<'PHP' + <<<'SPEC' +New statement call of an internal class imported with an aliased use statement: +- wrap the call in a prefixed namespace +- do not prefix the use and new statement +- transform the call into a FQ call +SPEC + , + 'payload' => <<<'PHP' +, - * Pádraic Brady - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'meta' => [ - 'title' => 'New statement call of a class imported via an aliased use statement in the global scope', - // Default values. If not specified will be the one used - 'prefix' => 'Humbug', - 'whitelist' => [], - ], - - [ - 'spec' => <<<'SPEC' -New statement call of a class belonging to the global namespace imported via an aliased use statement: -- do not touch the use statement (see tests related to the use statements of a class belonging to the global scope) -- transform the call into a FQ call -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a class belonging to the global namespace imported via an aliased use statement: -- do not touch the use statement (see tests related to the use statements of a class belonging to the global scope) -- do not touch the call -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -New statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the use statement -- prefix the call -- transform the call into a FQ call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the statement -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' New statement call of a class belonging to the global namespace imported via a use statement: -- do not touch the use statement (see tests related to the use statements of a class belonging to the global scope) +- prefix the namespace, use and new statements - transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' FQ new statement call of a class belonging to the global namespace imported via a use statement: -- do not touch the use statement (see tests related to the use statements of a class belonging to the global scope) +- prefix the namespace, use and new statements SPEC , 'payload' => <<<'PHP' <<<'SPEC' -New statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the use statement -- prefix the call -- transform the call into a FQ call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the statement -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes +New statement call of an internal class: +- wrap the call in a prefixed namespace +- do not prefix the use and new statement +- transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' New statement call of a class belonging to the global namespace: -- transform the call into a FQ call +- wrap everything in a prefixed namespace +- prefix the class +- transform the class in a FQCN call +prefix it SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a class belonging to the global namespace: -- do not prefix the call as can be part of the global namespace -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -New statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the call -- transform the call into a FQ call -- See `scope.inc.php` for the built-in global whitelisted classes +New statement call of a class belonging to the global namespace: +- wrap everything in a prefixed namespace +- do not prefix the class +- transform the class in a FQCN call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes +FQ new statement call of a class belonging to the global namespace: +- wrap everything in a prefixed namespace +- prefix the class SPEC , 'payload' => <<<'PHP' <<<'SPEC' New statement call of a namespaced class partially imported with an aliased use statement: -- do not touch the use statement: see tests for the use statements as to why -- prefix the call +- prefix the namespaces, use statement and the call - transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' New statement call of a namespaced class imported with an aliased use statement: -- prefix the use statement -- prefix the call +- prefix the namespaces, use statement and the call - transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a namespaced class partially imported with an aliased use statement: -- do not touch the use statement: see tests for the use statements and classes of the global namespace as to why -- prefix the call +FQ new statement call of a namespaced class with an aliased use statement: +- prefix the namespaces, use statement and the call +- transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a namespaced class imported with an aliased use statement: -- prefix the use statement -- do not touch the call: see tests for the use statements and classes of the global namespace as to why -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -New statement call of a whitelisted namespaced class partially imported with an aliased use statement: -- do not touch the use statement: see tests for the use statements as to why -- do not prefix the call +FQ new statement call of a class with an aliased use statement: +- prefix the namespaces, use statement and the call - transform the call into a FQ call SPEC , - 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' -New statement call of a whitelisted namespaced class imported with an aliased use statement: -- prefix the use statement -- do not prefix the call +New statement call of a whitelisted namespaced class partially imported with an aliased use statement: +- prefix each namespace and the call +- append the class_alias statement to the whitelisted class - transform the call into a FQ call SPEC , @@ -146,46 +208,46 @@ 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a whitelisted namespaced class partially imported with an aliased use statement: -- do not touch the use statement: see tests for the use statements as to why -- prefix the call: as it is a FQ the use statement is ignored -SPEC - , - 'whitelist' => ['Foo\Bar'], - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a whitelisted namespaced class imported with an aliased use statement: -- prefix the use statement -- do not prefix the call +New statement call of a whitelisted namespaced class imported with an aliased use statement: +- prefix the namespaces, use statement and the call +- append the class_alias statement to the whitelisted class - transform the call into a FQ call SPEC , @@ -193,14 +255,28 @@ 'payload' => <<<'PHP' <<<'SPEC' New statement call of a namespaced class partially imported with a use statement: -- do not touch the use statement: see tests for the use statements as to why -- prefix the call +- prefix the namespaces and the call - transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -New statement call of a namespaced class imported with a use statement: -- prefix the use statement -- prefix the call +New statement call of a whitelisted namespaced class partially imported with a use statement: +- prefix the namespaces and the call - transform the call into a FQ call SPEC , + 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' FQ new statement call of a namespaced class partially imported with a use statement: -- do not touch the use statement: see tests for the use statements and classes of the global namespace as to why -- prefix the call +- prefix the namespaces and the call +- transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a namespaced class imported with a use statement: -- prefix the use statement -- do not touch the call: see tests for the use statements and classes of the global namespace as to why +FQ new statement call of a whitelisted namespaced class partially imported with a use statement: +- prefix the namespaces and the call +- transform the call into a FQ call SPEC , + 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' -New statement call of a whitelisted namespaced class partially imported with a use statement: -- do not touch the use statement: see tests for the use statements as to why -- do not prefix the call +New statement call of a namespaced class imported with a use statement: +- prefix the namespaces and the call - transform the call into a FQ call SPEC , - 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' New statement call of a whitelisted namespaced class imported with a use statement: -- prefix the use statement -- do not prefix the call +- prefix the namespaces and the call +- do not prefix the use statement of the whitelisted class +- append the class_alias statement to the class - transform the call into a FQ call SPEC , @@ -146,37 +257,85 @@ 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a whitelisted namespaced class partially imported with a use statement: -- do not touch the use statement: see tests for the use statements as to why -- do not prefix the call +FQ new statement call of a namespaced class imported with a use statement: +- prefix the namespaces and the call +- transform the call into a FQ call SPEC , - 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' FQ new statement call of a whitelisted namespaced class imported with a use statement: -- prefix the use statement -- do not prefix the call +- prefix the namespaces and the call +- do not prefix the use statement of the whitelisted class +- append the class_alias statement to the class - transform the call into a FQ call SPEC , @@ -193,14 +353,41 @@ 'payload' => <<<'PHP' <<<'SPEC' New statement call of a namespaced class: +- prefix the namespace - prefix the call - transform the call into a FQ call SPEC @@ -30,10 +31,23 @@ 'payload' => <<<'PHP' <<<'SPEC' FQ new statement call of a namespaced class: +- prefix the namespace - prefix the call SPEC , 'payload' => <<<'PHP' <<<'SPEC' -New statement call of a namespaced class which has been whitelisted: -- do not prefix the call +New statement call of a whitelisted namespaced class: +- prefix the namespace +- append the class_alias for the whitelisted class +- prefix the call - transform the call into a FQ call SPEC , @@ -68,30 +98,61 @@ 'payload' => <<<'PHP' <<<'SPEC' -FQ new statement call of a namespaced class which has been whitelisted: -- do not prefix the call +FQ new statement call of a whitelisted namespaced class: +- prefix the namespace +- append the class_alias for the whitelisted class +- prefix the call +- transform the call into a FQ call SPEC , 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' -New statement call of a class which has been whitelisted and belongs to the global namespace: -- prefix the namespace -- prefix the call: see `scope.inc.php` for the built-in global whitelisted classes -- transform the call into a FQ call -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a class which has been whitelisted and belongs to the global namespace: -- prefix the namespace -- prefix the call: see `scope.inc.php` for the built-in global whitelisted classes -- transform the call into a FQ call -SPEC - , - 'payload' => <<<'PHP' - <<<'PHP' <<<'SPEC' -FQ new statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the namespace -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'PHP' <<<'PHP' <<<'SPEC' -New statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the use statement -- prefix the call -- transform the call into a FQ call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a class belonging to the global namespace which has been whitelisted: -- prefix the statement -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes -SPEC - , - 'payload' => <<<'PHP' - <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' -New statement call of a whitelisted class: -- prefix the namespace -- do not prefix the call -- transform the call into a FQ call -SPEC - , - 'whitelist' => ['X\Foo\Bar'], - 'payload' => <<<'PHP' - <<<'SPEC' -FQ new statement call of a non-whitelisted class: -- prefix the namespace -- prefix the call -SPEC - , - 'whitelist' => ['X\Foo\Bar'], - 'payload' => <<<'PHP' -name; } } -class B extends \A +class B extends \Humbug\A { const FOO = 'BAR'; public function __construct(string $name) @@ -91,8 +93,8 @@ public function __construct(string $name) parent::FOO; } } -\B::test(); -echo (new \B('yo'))->getName() . \PHP_EOL; +\Humbug\B::test(); +echo (new \Humbug\B('yo'))->getName() . \PHP_EOL; PHP ], @@ -138,6 +140,8 @@ public function __construct(string $name) { } namespace { + use Foo\B; + B::test(); echo (new B('yo'))->getName().PHP_EOL; } @@ -145,39 +149,40 @@ public function __construct(string $name) { ---- name = $name; - } - public static function test() - { - self::FOO; - static::FOO; - } - public function getName() : string - { - return $this->name; - } + $this->name = $name; } - class B extends \Humbug\Foo\A + public static function test() { - const FOO = 'BAR'; - public function __construct(string $name) - { - parent::__construct($name); - parent::FOO; - } + self::FOO; + static::FOO; + } + public function getName() : string + { + return $this->name; } } -namespace { - \B::test(); - echo (new \B('yo'))->getName() . \PHP_EOL; +class B extends \Humbug\Foo\A +{ + const FOO = 'BAR'; + public function __construct(string $name) + { + parent::__construct($name); + parent::FOO; + } } +namespace Humbug; + +use Humbug\Foo\B; +\Humbug\Foo\B::test(); +echo (new \Humbug\Foo\B('yo'))->getName() . \PHP_EOL; PHP ], diff --git a/specs/special-keywords/self-static-parent-method.php b/specs/special-keywords/self-static-parent-method.php index 679fe3b9..ee53493f 100644 --- a/specs/special-keywords/self-static-parent-method.php +++ b/specs/special-keywords/self-static-parent-method.php @@ -75,6 +75,8 @@ public static function who() { ---- getName() . \PHP_EOL; +\Humbug\B::test(); +echo (new \Humbug\B('yo'))->getName() . \PHP_EOL; PHP ], @@ -175,6 +177,8 @@ public static function who() { } namespace { + use Foo\B; + B::test(); echo (new B('yo'))->getName().PHP_EOL; } @@ -182,54 +186,55 @@ public static function who() { ---- name = $name; - } - public static function who() - { - echo __METHOD__ . PHP_EOL; - } - public static function test() - { - self::who(); - static::who(); - } - public function getName() : string - { - return $this->name; - } - public function create() : self - { - return new static(); - return new self(); - return parent::create(); - } - public function with(self $arg) : self - { - return $arg; - } + $this->name = $name; } - class B extends \Humbug\Foo\A + public static function who() { - public function __construct(string $name) - { - parent::__construct($name); - } - public static function who() - { - echo __METHOD__ . PHP_EOL; - } + echo __METHOD__ . PHP_EOL; + } + public static function test() + { + self::who(); + static::who(); + } + public function getName() : string + { + return $this->name; + } + public function create() : self + { + return new static(); + return new self(); + return parent::create(); + } + public function with(self $arg) : self + { + return $arg; } } -namespace { - \B::test(); - echo (new \B('yo'))->getName() . \PHP_EOL; +class B extends \Humbug\Foo\A +{ + public function __construct(string $name) + { + parent::__construct($name); + } + public static function who() + { + echo __METHOD__ . PHP_EOL; + } } +namespace Humbug; + +use Humbug\Foo\B; +\Humbug\Foo\B::test(); +echo (new \Humbug\Foo\B('yo'))->getName() . \PHP_EOL; PHP ], diff --git a/specs/special-keywords/self-static-parent-static-var.php b/specs/special-keywords/self-static-parent-static-var.php index db252039..dafc506f 100644 --- a/specs/special-keywords/self-static-parent-static-var.php +++ b/specs/special-keywords/self-static-parent-static-var.php @@ -64,6 +64,8 @@ public function __construct(string $name) { ---- name; } } -class B extends \A +class B extends \Humbug\A { static $foo = 'BAR'; public function __construct(string $name) @@ -91,8 +93,8 @@ public function __construct(string $name) parent::$foo; } } -\B::test(); -echo (new \B('yo'))->getName() . \PHP_EOL; +\Humbug\B::test(); +echo (new \Humbug\B('yo'))->getName() . \PHP_EOL; PHP ], @@ -138,6 +140,8 @@ public function __construct(string $name) { } namespace { + use Foo\B; + B::test(); echo (new B('yo'))->getName().PHP_EOL; } @@ -145,39 +149,40 @@ public function __construct(string $name) { ---- name = $name; - } - public static function test() - { - self::$foo; - static::$foo; - } - public function getName() : string - { - return $this->name; - } + $this->name = $name; } - class B extends \Humbug\Foo\A + public static function test() { - static $foo = 'BAR'; - public function __construct(string $name) - { - parent::__construct($name); - parent::$foo; - } + self::$foo; + static::$foo; + } + public function getName() : string + { + return $this->name; } } -namespace { - \B::test(); - echo (new \B('yo'))->getName() . \PHP_EOL; +class B extends \Humbug\Foo\A +{ + static $foo = 'BAR'; + public function __construct(string $name) + { + parent::__construct($name); + parent::$foo; + } } +namespace Humbug; + +use Humbug\Foo\B; +\Humbug\Foo\B::test(); +echo (new \Humbug\Foo\B('yo'))->getName() . \PHP_EOL; PHP ], diff --git a/specs/static-method/global-scope-single-part-with-single-level-use-statement-and-alias.php b/specs/static-method/global-scope-single-part-with-single-level-use-statement-and-alias.php index 3b1603cb..71419e48 100644 --- a/specs/static-method/global-scope-single-part-with-single-level-use-statement-and-alias.php +++ b/specs/static-method/global-scope-single-part-with-single-level-use-statement-and-alias.php @@ -30,14 +30,21 @@ 'payload' => <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' -Static method call statement of a class belonging to the global namespace which has been whitelisted: -- prefix the call +Static method call statement of an internal class : +- do not prefix the call - transform the call into a FQ call -- See `scope.inc.php` for the built-in global whitelisted classes SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ static method call statement of a class belonging to the global namespace which has been whitelisted: -- prefix the call -- See `scope.inc.php` for the built-in global whitelisted classes +FQ static method call statement of an internal class : +- do not prefix the call +- transform the call into a FQ call SPEC , 'payload' => <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' - <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'PHP' <<<'SPEC' -Use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. +Use statement of a class belonging to the global scope: +- wrap everything in a prefixed namespace +- prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. +FQ use statement of a class belonging to the global scope: +- wrap everything in a prefixed namespace +- prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of a (global) whitelisted class belonging to the global scope: -- prefix the use statement -- see `scope.inc.php` for the built-in global whitelisted classes +Use statement of an internal class belonging to the global scope: +- wrap everything in a prefixed namespace +- do not prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of a (global) whitelisted class belonging to the global scope which is already whitelisted: -- do nothing -- see `scope.inc.php` for the built-in global whitelisted classes +FQ use statement of an internal class belonging to the global scope: +- wrap everything in a prefixed namespace +- do not prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. +Use statement of a class belonging to the global scope: +- wrap the statement in a prefixed namespace +- prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -FQ use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. -SPEC - , - 'payload' => <<<'PHP' - <<<'SPEC' -FQ use statement of a class belonging to the global scope. Do nothing as this can belong to a PHP built-in class. +FQ use statement of a class belonging to the global scope: +- wrap the statement in a prefixed namespace +- prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of a (global) whitelisted class belonging to the global scope: -- prefix the use statement -- see `scope.inc.php` for the built-in global whitelisted classes +Use statement of an internal class belonging to the global scope: +- wrap the statement in a prefixed namespace +- do not prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of a (global) whitelisted class belonging to the global scope which is already whitelisted: -- do nothing -- see `scope.inc.php` for the built-in global whitelisted classes +Use statement of an internal class belonging to the global scope: +- wrap the statement in a prefixed namespace +- do not prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' Use statement of two-level class: +- prefix the namespaces - prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' -Use statement of two-level class which has been already prefixed: -- do nothing +Already prefixed use statement of two-level class: +- prefix the namespaces +- prefix the use statement SPEC , 'payload' => <<<'PHP' <<<'SPEC' Use statement of two-level class which has been whitelisted: -- prefix the use statement: only actual usage of the class will be whitelisted +- prefix the namespaces +- append the class_alias statement to the whitelisted class +- do not prefix the use statement SPEC , 'whitelist' => ['Foo\Bar'], 'payload' => <<<'PHP' <<<'SPEC' Constant use statement for a constant belonging to the global namespace: -- prefix the use statement: as it is extremely rare to use a `use const` statement for a built-in const from the -global scope, we can relatively safely assume it is a user-land declare static-method which should be prefixed. +- prefix the use statement SPEC , 'payload' => <<<'PHP' @@ -35,8 +34,31 @@ ---- <<<'SPEC' +Constant use statement for an internal constant belonging to the global namespace: +- prefix the use statement +SPEC + , + 'payload' => <<<'PHP' +getPatchers(), $config->getWhitelist(), - $config->getGlobalNamespaceWhitelister(), $input->getOption(self::STOP_ON_FAILURE_OPT), $logger ); @@ -172,7 +171,6 @@ private function scopeFiles( string $output, array $patchers, array $whitelist, - Closure $globalNamespaceWhitelister, bool $stopOnFailure, ConsoleLogger $logger ): void { @@ -200,7 +198,6 @@ private function scopeFiles( $prefix, $patchers, $whitelist, - $globalNamespaceWhitelister, $stopOnFailure, $logger ); @@ -231,7 +228,6 @@ function ($a, $b) { * @param string $prefix * @param callable[] $patchers * @param string[] $whitelist - * @param callable $globalWhitelister * @param bool $stopOnFailure * @param ConsoleLogger $logger */ @@ -242,12 +238,11 @@ private function scopeFile( string $prefix, array $patchers, array $whitelist, - callable $globalWhitelister, bool $stopOnFailure, ConsoleLogger $logger ): void { try { - $scoppedContent = $this->scoper->scope($inputFilePath, $inputContents, $prefix, $patchers, $whitelist, $globalWhitelister); + $scoppedContent = $this->scoper->scope($inputFilePath, $inputContents, $prefix, $patchers, $whitelist); } catch (Throwable $error) { $exception = new ParsingException( sprintf( diff --git a/src/Console/Command/SelfUpdateCommand.php b/src/Console/Command/SelfUpdateCommand.php index 64244871..329da5cf 100644 --- a/src/Console/Command/SelfUpdateCommand.php +++ b/src/Console/Command/SelfUpdateCommand.php @@ -16,7 +16,7 @@ use Humbug\PhpScoper\Logger\UpdateConsoleLogger; use Humbug\SelfUpdate\Updater; -use PHAR; +use Phar; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -187,6 +187,6 @@ private function printCurrentStableVersion(): void private function getLocalPharName(): string { - return basename(PHAR::running()); + return basename(Phar::running()); } } diff --git a/src/Console/Configuration.php b/src/Console/Configuration.php index 486c0711..933bb9be 100644 --- a/src/Console/Configuration.php +++ b/src/Console/Configuration.php @@ -39,7 +39,6 @@ final class Configuration private $filesWithContents; private $patchers; private $whitelist; - private $globalNamespaceWhitelister; /** * @param string|null $path Absolute path to the configuration file. @@ -69,15 +68,11 @@ public static function load(string $path = null, array $paths = []): self $patchers = self::retrievePatchers($config); $whitelist = self::retrieveWhitelist($config); - $globalNamespace = self::retrieveGlobalNamespaceWhitelisters($config); - $globalWhitelister = self::createGlobalWhitelister($globalNamespace); - $finders = self::retrieveFinders($config); $filesFromPaths = self::retrieveFilesFromPaths($paths); - $filesWithContents = self::retrieveFilesWithContents(iterables_to_iterator($filesFromPaths, ...$finders)); - return new self($path, $filesWithContents, $patchers, $whitelist, $globalWhitelister); + return new self($path, $filesWithContents, $patchers, $whitelist); } /** @@ -94,14 +89,12 @@ private function __construct( ?string $path, array $filesWithContents, array $patchers, - array $whitelist, - Closure $globalNamespaceWhitelisters + array $whitelist ) { $this->path = $path; $this->filesWithContents = $filesWithContents; $this->patchers = $patchers; $this->whitelist = $whitelist; - $this->globalNamespaceWhitelister = $globalNamespaceWhitelisters; } public function withPaths(array $paths): self @@ -118,8 +111,7 @@ public function withPaths(array $paths): self $this->path, array_merge($this->filesWithContents, $filesWithContents), $this->patchers, - $this->whitelist, - $this->globalNamespaceWhitelister + $this->whitelist ); } @@ -146,11 +138,6 @@ public function getWhitelist(): array return $this->whitelist; } - public function getGlobalNamespaceWhitelister(): Closure - { - return $this->globalNamespaceWhitelister; - } - private static function validateConfigKeys(array $config): void { array_map( @@ -237,67 +224,6 @@ private static function retrieveWhitelist(array $config): array return $whitelist; } - private static function retrieveGlobalNamespaceWhitelisters(array $config): array - { - if (false === array_key_exists(self::GLOBAL_NAMESPACE_KEYWORD, $config)) { - return []; - } - - $globalNamespace = $config[self::GLOBAL_NAMESPACE_KEYWORD]; - - if (false === is_array($globalNamespace)) { - throw new InvalidArgumentException( - sprintf( - 'Expected "global_namespace" to be an array, found "%s" instead.', - gettype($globalNamespace) - ) - ); - } - - foreach ($globalNamespace as $index => $className) { - if (is_string($className) || is_callable($className)) { - continue; - } - - throw new InvalidArgumentException( - sprintf( - 'Expected "global_namespace" to be an array of callables or strings, the "%d" element ' - .'is not.', - $index - ) - ); - } - - return $globalNamespace; - } - - /** - * @param string[]|callable[] $globalNamespaceWhitelist - * - * @return Closure - */ - private static function createGlobalWhitelister(array $globalNamespaceWhitelist): Closure - { - return function (string $className) use ($globalNamespaceWhitelist): bool { - foreach ($globalNamespaceWhitelist as $whitelister) { - if (is_string($whitelister)) { - if ($className === $whitelister) { - return true; - } else { - continue; - } - } - - /** @var callable $whitelister */ - if (true === $whitelister($className)) { - return true; - } - } - - return false; - }; - } - private static function retrieveFinders(array $config): array { if (false === array_key_exists(self::FINDER_KEYWORD, $config)) { diff --git a/src/NodeTraverser.php b/src/NodeTraverser.php new file mode 100644 index 00000000..d5f45133 --- /dev/null +++ b/src/NodeTraverser.php @@ -0,0 +1,163 @@ +prefix = $prefix; + } + + + /** + * @inheritdoc + */ + public function traverse(array $nodes) + { + $nodes = $this->wrapInNamespace($nodes); + $nodes = $this->replaceGroupUseStatements($nodes); + + return parent::traverse($nodes); + } + + /** + * Wrap the statements in a namespace when necessary: + * + * ```php + * #!/usr/bin/env php + * $node) { + if ($node instanceof Declare_ || $node instanceof InlineHTML) { + continue; + } + + $firstRealStatementIndex = $i; + $realStatements = array_slice($nodes, $i); + + break; + } + + $firstRealStatement = current($realStatements); + + if (false !== $firstRealStatement && false === ($firstRealStatement instanceof Namespace_)) { + $wrappedStatements = new Namespace_(null, $realStatements); + + array_splice($nodes, $firstRealStatementIndex, count($realStatements), [$wrappedStatements]); + } + + return $nodes; + } + + /** + * @param Node[] $nodes + * + * @return Node[] + */ + private function replaceGroupUseStatements(array $nodes): array + { + foreach ($nodes as $node) { + if (false === ($node instanceof Namespace_)) { + continue; + } + + /** @var Namespace_ $node */ + $statements = $node->stmts; + + $newStatements = []; + + foreach ($statements as $statement) { + if ($statement instanceof GroupUse) { + $uses_ = $this->createUses_($statement); + + array_splice($newStatements, count($newStatements), 0, $uses_); + } else { + $newStatements[] = $statement; + } + } + + $node->stmts = $newStatements; + } + + return $nodes; + } + + /** + * @param GroupUse $node + * + * @return Use_[] + */ + private function createUses_(GroupUse $node): array + { + return array_map( + function (UseUse $use) use ($node): Use_ { + $newUse = new UseUse( + Name::concat($node->prefix, $use->name, $use->name->getAttributes()), + $use->alias, + $use->type, + $use->getAttributes() + ); + + return new Use_( + [$newUse], + $node->type, + $node->getAttributes() + ); + }, + $node->uses + ); + } +} \ No newline at end of file diff --git a/src/NodeVisitor/NameStmtPrefixer.php b/src/NodeVisitor/NameStmtPrefixer.php index 4d256f47..129fecde 100644 --- a/src/NodeVisitor/NameStmtPrefixer.php +++ b/src/NodeVisitor/NameStmtPrefixer.php @@ -25,7 +25,9 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Interface_; use PhpParser\NodeVisitorAbstract; +use Roave\BetterReflection\Reflector\ClassReflector; /** * ``` @@ -50,25 +52,25 @@ final class NameStmtPrefixer extends NodeVisitorAbstract private $prefix; private $whitelist; - private $globalWhitelister; private $nameResolver; + private $classReflector; /** * @param string $prefix * @param string[] $whitelist - * @param callable $globalWhitelister * @param FullyQualifiedNameResolver $nameResolver + * @param ClassReflector $classReflector */ public function __construct( string $prefix, array $whitelist, - callable $globalWhitelister, - FullyQualifiedNameResolver $nameResolver + FullyQualifiedNameResolver $nameResolver, + ClassReflector $classReflector ) { $this->prefix = $prefix; - $this->whitelist = $whitelist; - $this->globalWhitelister = $globalWhitelister; + //$this->whitelist = $whitelist; $this->nameResolver = $nameResolver; + $this->classReflector = $classReflector; } /** @@ -94,12 +96,14 @@ private function prefixName(Name $name): Node || $parentNode instanceof StaticCall || $parentNode instanceof New_ || $parentNode instanceof Class_ + || $parentNode instanceof Interface_ ) ) { return $name; } - if (( + if ( + ( $parentNode instanceof FuncCall || $parentNode instanceof StaticCall || $parentNode instanceof ClassConstFetch @@ -122,13 +126,7 @@ private function prefixName(Name $name): Node // Check if the class can be prefixed if (false === ($parentNode instanceof ConstFetch || $parentNode instanceof FuncCall)) { - if (1 === count($resolvedName->parts) - && false === ($this->globalWhitelister)($resolvedName->toString()) - ) { - return $resolvedName; - } elseif (1 < count($resolvedName->parts) - && in_array($resolvedName->toString(), $this->whitelist) - ) { + if ($this->classReflector->reflect($resolvedName->toString())->isInternal()) { return $resolvedName; } } diff --git a/src/NodeVisitor/NamespaceStmtPrefixer.php b/src/NodeVisitor/NamespaceStmtPrefixer.php index 56a1779c..2f0dbe81 100644 --- a/src/NodeVisitor/NamespaceStmtPrefixer.php +++ b/src/NodeVisitor/NamespaceStmtPrefixer.php @@ -41,47 +41,45 @@ final class NamespaceStmtPrefixer extends NodeVisitorAbstract private $prefix; private $namespaceStatements; private $hasWhitelistedNode; - private $globalWhitelister; - public function __construct( - string $prefix, - NamespaceStmtCollection $namespaceStatements, - callable $globalWhitelister - ) { + public function __construct(string $prefix, NamespaceStmtCollection $namespaceStatements) + { $this->prefix = $prefix; $this->namespaceStatements = $namespaceStatements; - $this->globalWhitelister = $globalWhitelister; } - /** - * @inheritdoc - */ - public function beforeTraverse(array $nodes) - { - $this->hasWhitelistedNode = $this->hasWhitelistedNode($nodes); - } +// /** +// * @inheritdoc +// */ +// public function beforeTraverse(array $nodes) +// { +// $this->hasWhitelistedNode = $this->hasWhitelistedNode($nodes); +// } /** * @inheritdoc */ public function enterNode(Node $node): Node { + $x = ''; + return ($node instanceof Namespace_) ? $this->prefixNamespaceStmt($node) : $node; } - - /** - * @inheritdoc - */ - public function leaveNode(Node $node) - { - return ( - !$this->hasWhitelistedNode - || $node instanceof Namespace_ - || AppendParentNode::hasParent($node) - ) ? $node : $this->wrapNamespace($node); - } +// +// /** +// * @inheritdoc +// */ +// public function leaveNode(Node $node) +// { +// $x = ''; +// +// return ( +// 0 === $this->namespaceStatements->count() +// && false === AppendParentNode::hasParent($node) +// ) ? $this->wrapNamespace($node) : $node; +// } private function prefixNamespaceStmt(Namespace_ $namespace): Node { @@ -100,8 +98,8 @@ private function prefixNamespaceStmt(Namespace_ $namespace): Node private function wrapNamespace(Node $node): Node { + return new Namespace_(new Node\Name($this->prefix), [$node]); if ($this->isWhitelistedNode($node)) { - return new Namespace_(new Node\Name($this->prefix), [$node]); } // Anything else needs to be wrapped with global namespace. @@ -126,9 +124,7 @@ private function hasWhitelistedNode(array $nodes): bool private function isWhitelistedNode(Node $node) { - if (($node instanceof Class_ || $node instanceof Interface_) - && ($this->globalWhitelister)($node->name) - ) { + if (($node instanceof Class_ || $node instanceof Interface_)) { return true; } @@ -146,13 +142,15 @@ private function isWhitelistedNode(Node $node) private function shouldPrefixStmt(Namespace_ $namespace): bool { + return null === $namespace->name || (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()); + if (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()) { return true; } - if (null === $namespace->name && $this->hasWhitelistedNode([$namespace])) { - return true; - } +// if (null === $namespace->name && $this->hasWhitelistedNode([$namespace])) { +// return true; +// } return false; } diff --git a/src/NodeVisitor/StringScalarPrefixer.php b/src/NodeVisitor/StringScalarPrefixer.php index f6dd517e..00a864a4 100644 --- a/src/NodeVisitor/StringScalarPrefixer.php +++ b/src/NodeVisitor/StringScalarPrefixer.php @@ -14,7 +14,6 @@ namespace Humbug\PhpScoper\NodeVisitor; -use Humbug\PhpScoper\NodeVisitor\Resolver\FullyQualifiedNameResolver; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; @@ -22,6 +21,7 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\String_; use PhpParser\NodeVisitorAbstract; +use Roave\BetterReflection\Reflector\ClassReflector; /** * Prefixes the string scalar values. @@ -41,28 +41,24 @@ final class StringScalarPrefixer extends NodeVisitorAbstract private $prefix; private $whitelistedFunctions; private $whitelist; - private $globalWhitelister; - private $nameResolver; + private $classReflector; /** - * @param string $prefix - * @param string[] $whitelistedFunctions - * @param string[] $whitelist - * @param callable $globalWhitelister - * @param FullyQualifiedNameResolver $nameResolver + * @param string $prefix + * @param string[] $whitelistedFunctions + * @param string[] $whitelist + * @param ClassReflector $classReflector */ public function __construct( string $prefix, array $whitelistedFunctions, array $whitelist, - callable $globalWhitelister, - FullyQualifiedNameResolver $nameResolver + ClassReflector $classReflector ) { $this->prefix = $prefix; $this->whitelistedFunctions = $whitelistedFunctions; $this->whitelist = $whitelist; - $this->globalWhitelister = $globalWhitelister; - $this->nameResolver = $nameResolver; + $this->classReflector = $classReflector; } /** @@ -109,7 +105,7 @@ private function prefixStringScalar(String_ $string): Node $newStringName = $stringName; // Check if the class can be prefixed: class from the global namespace } elseif (1 === count($stringName->parts) - && false === ($this->globalWhitelister)($stringName->toString()) + && false === $this->classReflector->reflect($stringName->toString())->isInternal() ) { $newStringName = $stringName; // Check if the class can be prefixed: regular class diff --git a/src/NodeVisitor/UseStmt/GroupUseStmtTransformer.php b/src/NodeVisitor/UseStmt/GroupUseStmtTransformer.php deleted file mode 100644 index 19c51069..00000000 --- a/src/NodeVisitor/UseStmt/GroupUseStmtTransformer.php +++ /dev/null @@ -1,87 +0,0 @@ -, - * Pádraic Brady - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Humbug\PhpScoper\NodeVisitor\UseStmt; - -use PhpParser\Node\Name; -use PhpParser\Node\Stmt\GroupUse; -use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; -use PhpParser\NodeVisitorAbstract; - -/** - * Transforms the grouped use statements into regular use statements which are easier to work with. - * - * ``` - * use A\B\{C\D, function b\c, const D}; - * ``` - * - * => - * - * ``` - * use Humbug\A\B\C\D; - * use function Humbug\A\B\b\c; - * use const Humbug\A\B\D; - * ``` - * - * @private - */ -final class GroupUseStmtTransformer extends NodeVisitorAbstract -{ - /** - * @inheritdoc - */ - public function beforeTraverse(array $nodes): array - { - $newNodes = []; - - foreach ($nodes as $node) { - if ($node instanceof GroupUse) { - $uses_ = $this->createUses_($node); - - array_splice($newNodes, count($newNodes), 0, $uses_); - } else { - $newNodes[] = $node; - } - } - - return $newNodes; - } - - /** - * @param GroupUse $node - * - * @return Use_[] - */ - public function createUses_(GroupUse $node): array - { - return array_map( - function (UseUse $use) use ($node): Use_ { - $newUse = new UseUse( - Name::concat($node->prefix, $use->name, $use->name->getAttributes()), - $use->alias, - $use->type, - $use->getAttributes() - ); - - return new Use_( - [$newUse], - $node->type, - $node->getAttributes() - ); - }, - $node->uses - ); - } -} diff --git a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php index 51ae340d..54260fbf 100644 --- a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php +++ b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php @@ -20,6 +20,9 @@ use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\UseUse; use PhpParser\NodeVisitorAbstract; +use Roave\BetterReflection\BetterReflection; +use Roave\BetterReflection\Reflection\ReflectionClass; +use Roave\BetterReflection\Reflector\ClassReflector; /** * Prefixes the use statements. @@ -28,18 +31,18 @@ final class UseStmtPrefixer extends NodeVisitorAbstract { private $prefix; private $whitelist; - private $globalWhitelister; + private $classReflector; /** - * @param string $prefix - * @param string[] $whitelist - * @param callable $globalWhitelister + * @param string $prefix + * @param string[] $whitelist + * @param ClassReflector $classReflector */ - public function __construct(string $prefix, array $whitelist, callable $globalWhitelister) + public function __construct(string $prefix, array $whitelist, ClassReflector $classReflector) { $this->prefix = $prefix; $this->whitelist = $whitelist; - $this->globalWhitelister = $globalWhitelister; + $this->classReflector = $classReflector; } /** @@ -71,7 +74,7 @@ private function shouldPrefixUseStmt(UseUse $use): bool if (1 === count($use->name->parts)) { return Use_::TYPE_NORMAL !== $useType - || ($this->globalWhitelister)($use->name->getFirst()) + || false === $this->classReflector->reflect($use->name->getFirst())->isInternal() ; } diff --git a/src/Scoper.php b/src/Scoper.php index bbdd17b5..f4527547 100644 --- a/src/Scoper.php +++ b/src/Scoper.php @@ -21,18 +21,15 @@ interface Scoper /** * Scope AKA. apply the given prefix to the file in the appropriate way. * - * @param string $filePath File to scope + * @param string $filePath File to scope * @param string $contents File contents - * @param string $prefix Prefix to apply to the file + * @param string $prefix Prefix to apply to the file * @param callable[] $patchers - * @param string[] $whitelist List of classes to exclude from the scoping. - * @param callable $globalWhitelister Closure taking a class name from the global namespace as an argument and - * returning a boolean which if `true` means the class should be scoped - * (i.e. is ignored) or scoped otherwise. + * @param string[] $whitelist List of classes to exclude from the scoping. * * @throws ParsingException * * @return string Contents of the file with the prefix applied */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string; + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string; } diff --git a/src/Scoper/Composer/InstalledPackagesScoper.php b/src/Scoper/Composer/InstalledPackagesScoper.php index b735ed9b..c437ee41 100644 --- a/src/Scoper/Composer/InstalledPackagesScoper.php +++ b/src/Scoper/Composer/InstalledPackagesScoper.php @@ -32,10 +32,10 @@ public function __construct(Scoper $decoratedScoper) * * {@inheritdoc} */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { if (1 !== preg_match(self::$filePattern, $filePath)) { - return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $globalWhitelister); + return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); } $decodedJson = json_decode($contents, true); diff --git a/src/Scoper/Composer/JsonFileScoper.php b/src/Scoper/Composer/JsonFileScoper.php index 792703cd..2410be28 100644 --- a/src/Scoper/Composer/JsonFileScoper.php +++ b/src/Scoper/Composer/JsonFileScoper.php @@ -30,10 +30,10 @@ public function __construct(Scoper $decoratedScoper) * * {@inheritdoc} */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { if (1 !== preg_match('/composer\.json$/', $filePath)) { - return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $globalWhitelister); + return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); } $decodedJson = json_decode($contents, true); diff --git a/src/Scoper/NullScoper.php b/src/Scoper/NullScoper.php index de934442..306c34cc 100644 --- a/src/Scoper/NullScoper.php +++ b/src/Scoper/NullScoper.php @@ -21,7 +21,7 @@ final class NullScoper implements Scoper /** * @inheritdoc */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { return $contents; } diff --git a/src/Scoper/PatchScoper.php b/src/Scoper/PatchScoper.php index 5e5102ed..3a523b51 100644 --- a/src/Scoper/PatchScoper.php +++ b/src/Scoper/PatchScoper.php @@ -28,9 +28,9 @@ public function __construct(Scoper $decoratedScoper) /** * @inheritdoc */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { - $contents = $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $globalWhitelister); + $contents = $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); return array_reduce( $patchers, diff --git a/src/Scoper/PhpScoper.php b/src/Scoper/PhpScoper.php index 66afa1a8..94fda167 100644 --- a/src/Scoper/PhpScoper.php +++ b/src/Scoper/PhpScoper.php @@ -44,15 +44,15 @@ public function __construct(Parser $parser, Scoper $decoratedScoper, TraverserFa * * @throws PhpParserError */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { if (false === $this->isPhpFile($filePath, $contents)) { - return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $globalWhitelister); + return $this->decoratedScoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); } $statements = $this->parser->parse($contents); - $traverser = $this->traverserFactory->create($prefix, $whitelist, $globalWhitelister); + $traverser = $this->traverserFactory->create($prefix, $whitelist); $statements = $traverser->traverse($statements); diff --git a/src/Scoper/TraverserFactory.php b/src/Scoper/TraverserFactory.php index 233cfe31..cc77c3c7 100644 --- a/src/Scoper/TraverserFactory.php +++ b/src/Scoper/TraverserFactory.php @@ -14,12 +14,13 @@ namespace Humbug\PhpScoper\Scoper; +use Humbug\PhpScoper\NodeTraverser; use Humbug\PhpScoper\NodeVisitor; use Humbug\PhpScoper\NodeVisitor\Collection\NamespaceStmtCollection; use Humbug\PhpScoper\NodeVisitor\Collection\UseStmtCollection; use Humbug\PhpScoper\NodeVisitor\Resolver\FullyQualifiedNameResolver; -use PhpParser\NodeTraverser; use PhpParser\NodeTraverserInterface; +use Roave\BetterReflection\Reflector\ClassReflector; /** * @final @@ -33,36 +34,38 @@ class TraverserFactory 'class_exists', 'interface_exists', ]; + + private $classReflector; + + public function __construct(ClassReflector $classReflector) + { + $this->classReflector = $classReflector; + } /** - * @param string $prefix Prefix to apply to the files. - * @param string[] $whitelist List of classes to exclude from the scoping. - * @param callable $globalWhitelister Closure taking a class name from the global namespace as an argument and - * returning a boolean which if `true` means the class should be scoped - * (i.e. is ignored) or scoped otherwise. + * @param string $prefix Prefix to apply to the files. + * @param string[] $whitelist List of classes to exclude from the scoping. * * @return NodeTraverserInterface */ - public function create(string $prefix, array $whitelist, callable $globalWhitelister): NodeTraverserInterface + public function create(string $prefix, array $whitelist): NodeTraverserInterface { - $traverser = new NodeTraverser(); + $traverser = new NodeTraverser($prefix); $namespaceStatements = new NamespaceStmtCollection(); $useStatements = new UseStmtCollection(); $nameResolver = new FullyQualifiedNameResolver($namespaceStatements, $useStatements); - $traverser->addVisitor(new NodeVisitor\UseStmt\GroupUseStmtTransformer()); - $traverser->addVisitor(new NodeVisitor\AppendParentNode()); - $traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements, $globalWhitelister)); + $traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements)); $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements)); - $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $globalWhitelister)); + $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $this->classReflector)); - $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $globalWhitelister, $nameResolver)); - $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $globalWhitelister, $nameResolver)); + $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $nameResolver, $this->classReflector)); + $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $this->classReflector)); $traverser->addVisitor(new NodeVisitor\WhitelistedClassAppender($whitelist)); diff --git a/src/functions.php b/src/functions.php index a205a85f..614c7ae1 100644 --- a/src/functions.php +++ b/src/functions.php @@ -34,6 +34,7 @@ use PhpParser\Node; use PhpParser\Parser; use PhpParser\ParserFactory; +use Roave\BetterReflection\BetterReflection; use Symfony\Component\Console\Application as SymfonyApplication; use Symfony\Component\Filesystem\Filesystem; @@ -96,7 +97,9 @@ function create_scoper(): Scoper new NullScoper() ) ), - new TraverserFactory() + new TraverserFactory( + (new BetterReflection())->classReflector() + ) ) ); } diff --git a/src/scoper.inc.php.tpl b/src/scoper.inc.php.tpl index 087e761c..83db21e4 100644 --- a/src/scoper.inc.php.tpl +++ b/src/scoper.inc.php.tpl @@ -44,19 +44,6 @@ return [ }, ], - // By default, PHP-Scoper only prefixes code where the namespace is non-global. In other words, non-namespaced - // code is not prefixed. This leaves the majority of classes, functions and constants in PHP - and most extensions, - // untouched. - // - // This is not necessarily a desirable outcome for vendor dependencies which are also not namespaced. To ensure - // they are isolated, you can configure the following which can be a list of strings or callables taking a string - // (the class name) as an argument and return a boolean (true meaning the class is going to prefixed). - // - // For more, see https://github.com/humbug/php-scoper#global-namespace-whitelisting - 'global_namespace_whitelist' => [ - 'AppKernel', - ], - // PHP-Scoper's goal is to make sure that all code for a project lies in a distinct PHP namespace. However, you // may want to share a common API between the bundled code of your PHAR and the consumer code. For example if // you have a PHPUnit PHAR with isolated code, you still want the PHAR to be able to understand the diff --git a/tests/Console/Command/AddPrefixCommandTest.php b/tests/Console/Command/AddPrefixCommandTest.php index 0bf328f6..f919f733 100644 --- a/tests/Console/Command/AddPrefixCommandTest.php +++ b/tests/Console/Command/AddPrefixCommandTest.php @@ -204,8 +204,7 @@ public function test_scope_the_given_paths() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -268,8 +267,7 @@ public function test_let_the_file_unchanged_when_cannot_scope_a_file() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -282,8 +280,7 @@ public function test_let_the_file_unchanged_when_cannot_scope_a_file() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willThrow(new \RuntimeException('Scoping of the file failed')) ; @@ -343,8 +340,7 @@ public function test_do_not_scope_duplicated_given_paths() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -405,8 +401,7 @@ public function test_scope_the_given_paths_and_the_ones_found_by_the_finder() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedFileContents) ; @@ -460,8 +455,7 @@ function (string $prefix): bool { } ), [], - [], - Argument::type(Closure::class) + [] ) ->willReturn('') ; @@ -519,8 +513,7 @@ public function test_scope_the_current_working_directory_if_no_path_given() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -566,8 +559,7 @@ public function test_prefix_can_end_by_a_backslash() Argument::any(), 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn('') ; @@ -610,8 +602,7 @@ public function test_prefix_can_end_by_multiple_backslashes() Argument::any(), 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn('') ; @@ -669,8 +660,7 @@ public function test_an_output_directory_can_be_given() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -732,8 +722,7 @@ public function test_relative_output_directory_are_made_absolute() $inputContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -859,8 +848,7 @@ public function test_attempts_to_use_patch_file_in_current_directory() return true; }), - [], - Argument::type(Closure::class) + [] ) ->willReturn($prefixedContents) ; @@ -945,8 +933,7 @@ public function test_can_scope_projects_with_invalid_files() $fileContents, 'MyPrefix', [], - [], - Argument::type(Closure::class) + [] ) ->willThrow($scopingException = new RuntimeException('Could not scope file')) ; diff --git a/tests/Scoper/Composer/InstalledPackagesScoperTest.php b/tests/Scoper/Composer/InstalledPackagesScoperTest.php index 53eb6bc3..27891dc0 100644 --- a/tests/Scoper/Composer/InstalledPackagesScoperTest.php +++ b/tests/Scoper/Composer/InstalledPackagesScoperTest.php @@ -20,7 +20,6 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use function Humbug\PhpScoper\create_fake_patcher; -use function Humbug\PhpScoper\create_fake_whitelister; /** * @covers \Humbug\PhpScoper\Scoper\Composer\InstalledPackagesScoper @@ -40,12 +39,11 @@ public function test_delegates_scoping_to_the_decorated_scoper_if_is_not_a_insta $prefix = 'Humbug'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); /** @var Scoper|ObjectProphecy $decoratedScoperProphecy */ $decoratedScoperProphecy = $this->prophesize(Scoper::class); $decoratedScoperProphecy - ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister) + ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist) ->willReturn( $expected = 'Scoped content' ) @@ -55,7 +53,7 @@ public function test_delegates_scoping_to_the_decorated_scoper_if_is_not_a_insta $scoper = new InstalledPackagesScoper($decoratedScoper); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); @@ -74,9 +72,8 @@ public function test_it_prefixes_the_composer_autoloaders(string $fileContents, $prefix = 'Foo'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } diff --git a/tests/Scoper/Composer/JsonFileScoperTest.php b/tests/Scoper/Composer/JsonFileScoperTest.php index 9d2c5405..f3c4a269 100644 --- a/tests/Scoper/Composer/JsonFileScoperTest.php +++ b/tests/Scoper/Composer/JsonFileScoperTest.php @@ -20,7 +20,6 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use function Humbug\PhpScoper\create_fake_patcher; -use function Humbug\PhpScoper\create_fake_whitelister; /** * @covers \Humbug\PhpScoper\Scoper\Composer\JsonFileScoper @@ -40,12 +39,11 @@ public function test_delegates_scoping_to_the_decorated_scoper_if_is_not_a_compo $prefix = 'Humbug'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); /** @var Scoper|ObjectProphecy $decoratedScoperProphecy */ $decoratedScoperProphecy = $this->prophesize(Scoper::class); $decoratedScoperProphecy - ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister) + ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist) ->willReturn( $expected = 'Scoped content' ) @@ -55,7 +53,7 @@ public function test_delegates_scoping_to_the_decorated_scoper_if_is_not_a_compo $scoper = new JsonFileScoper($decoratedScoper); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); @@ -74,9 +72,8 @@ public function test_it_prefixes_the_composer_autoloaders(string $fileContents, $prefix = 'Foo'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } @@ -145,9 +142,8 @@ public function test_it_prefixes_psr0_autoloaders(string $fileContents, string $ $prefix = 'Foo'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } diff --git a/tests/Scoper/FakeScoper.php b/tests/Scoper/FakeScoper.php index 881d91ef..a32eea0b 100644 --- a/tests/Scoper/FakeScoper.php +++ b/tests/Scoper/FakeScoper.php @@ -22,7 +22,7 @@ final class FakeScoper implements Scoper /** * @inheritdoc */ - public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist, callable $globalWhitelister): string + public function scope(string $filePath, string $contents, string $prefix, array $patchers, array $whitelist): string { throw new LogicException(); } diff --git a/tests/Scoper/NullScoperTest.php b/tests/Scoper/NullScoperTest.php index 8648be08..b29f6e3c 100644 --- a/tests/Scoper/NullScoperTest.php +++ b/tests/Scoper/NullScoperTest.php @@ -40,11 +40,9 @@ public function test_returns_the_file_content_unchanged() $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $scoper = new NullScoper(); - $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } diff --git a/tests/Scoper/PatchScoperTest.php b/tests/Scoper/PatchScoperTest.php index 5c1dbffb..2dd67d4f 100644 --- a/tests/Scoper/PatchScoperTest.php +++ b/tests/Scoper/PatchScoperTest.php @@ -19,7 +19,6 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; -use function Humbug\PhpScoper\create_fake_whitelister; /** * @covers \Humbug\PhpScoper\Scoper\PatchScoper @@ -75,10 +74,8 @@ function (string $patcherFilePath, string $patcherPrefix, string $contents) use $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $this->decoratedScoperProphecy - ->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister) + ->scope($filePath, $contents, $prefix, $patchers, $whitelist) ->willReturn('Decorated scoper contents') ; @@ -86,7 +83,7 @@ function (string $patcherFilePath, string $patcherPrefix, string $contents) use $scoper = new PatchScoper($this->decoratedScoper); - $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); diff --git a/tests/Scoper/PhpScoperTest.php b/tests/Scoper/PhpScoperTest.php index a983715e..e7be0e98 100644 --- a/tests/Scoper/PhpScoperTest.php +++ b/tests/Scoper/PhpScoperTest.php @@ -25,10 +25,17 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; +use ReflectionClass; +use Reflector; +use Roave\BetterReflection\BetterReflection; +use Roave\BetterReflection\Reflection\Reflection; +use Roave\BetterReflection\Reflector\ClassReflector; +use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\StringSourceLocator; use Symfony\Component\Finder\Finder; use Throwable; use function Humbug\PhpScoper\create_fake_patcher; -use function Humbug\PhpScoper\create_fake_whitelister; use function Humbug\PhpScoper\create_parser; use function Humbug\PhpScoper\escape_path; @@ -92,17 +99,21 @@ class PhpScoperTest extends TestCase */ private $parser; + /** + * @var ClassReflector|ObjectProphecy + */ + private $classReflectorProphecy; + + /** + * @var ClassReflector + */ + private $classReflector; + /** * @inheritdoc */ public function setUp() { - $this->scoper = new PhpScoper( - create_parser(), - new FakeScoper(), - new TraverserFactory() - ); - $this->decoratedScoperProphecy = $this->prophesize(Scoper::class); $this->decoratedScoper = $this->decoratedScoperProphecy->reveal(); @@ -114,6 +125,17 @@ public function setUp() $this->parserProphecy = $this->prophesize(Parser::class); $this->parser = $this->parserProphecy->reveal(); + + $this->classReflectorProphecy = $this->prophesize(ClassReflector::class); + $this->classReflector = $this->classReflectorProphecy->reveal(); + + $this->scoper = new PhpScoper( + create_parser(), + new FakeScoper(), + new TraverserFactory( + $this->classReflector + ) + ); } public function test_is_a_Scoper() @@ -127,18 +149,23 @@ public function test_can_scope_a_PHP_file() $filePath = escape_path($this->tmp.'/file.php'); $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); $contents = <<<'PHP' +scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } @@ -150,10 +177,9 @@ public function test_does_not_scope_file_if_is_not_a_PHP_file() $prefix = 'Humbug'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); $this->decoratedScoperProphecy - ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister) + ->scope($filePath, $fileContents, $prefix, $patchers, $whitelist) ->willReturn( $expected = 'Scoped content' ) @@ -170,7 +196,7 @@ public function test_does_not_scope_file_if_is_not_a_PHP_file() $this->traverserFactory ); - $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $fileContents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); @@ -183,7 +209,6 @@ public function test_can_scope_a_PHP_file_with_the_wrong_extension() $filePath = escape_path($this->tmp.'/file'); $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); $contents = <<<'PHP' scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } @@ -210,7 +237,6 @@ public function test_can_scope_PHP_binary_files() $filePath = escape_path($this->tmp.'/hello'); $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); $contents = <<<'PHP' #!/usr/bin/env php @@ -222,11 +248,13 @@ public function test_can_scope_PHP_binary_files() $expected = <<<'PHP' #!/usr/bin/env php scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); } @@ -241,8 +269,6 @@ public function test_does_not_scope_a_non_PHP_binary_files() $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); - $contents = <<<'PHP' #!/usr/bin/env bash decoratedScoperProphecy - ->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister) + ->scope($filePath, $contents, $prefix, $patchers, $whitelist) ->willReturn( $expected = 'Scoped content' ) @@ -268,7 +294,7 @@ public function test_does_not_scope_a_non_PHP_binary_files() $this->traverserFactory ); - $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->assertSame($expected, $actual); @@ -288,10 +314,9 @@ public function test_cannot_scope_an_invalid_PHP_file() $prefix = 'Humbug'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); try { - $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $this->fail('Expected exception to have been thrown.'); } catch (PhpParserError $error) { @@ -314,10 +339,9 @@ public function test_creates_a_new_traverser_for_each_file() $prefix = 'Humbug'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); $this->decoratedScoperProphecy - ->scope(Argument::any(), Argument::any(), $prefix, $patchers, $whitelist, $whitelister) + ->scope(Argument::any(), Argument::any(), $prefix, $patchers, $whitelist) ->willReturn( $expected = 'Scoped content' ) @@ -378,7 +402,7 @@ function (...$args) use (&$i): bool { ); foreach ($files as $file => $contents) { - $scoper->scope($file, $contents, $prefix, $patchers, $whitelist, $whitelister); + $scoper->scope($file, $contents, $prefix, $patchers, $whitelist); } $this->parserProphecy->parse(Argument::cetera())->shouldHaveBeenCalledTimes(2); @@ -396,11 +420,48 @@ public function test_can_scope_valid_files(string $spec, string $contents, strin $patchers = [create_fake_patcher()]; - $whitelister = function (string $className) { - return 'AppKernel' === $className; - }; + $nonInternalReflectionProphecy = $this->prophesize(ReflectionClass::class); + $nonInternalReflectionProphecy->willImplement(Reflection::class); + $nonInternalReflectionProphecy->isInternal()->willReturn(false); + $nonInternalReflection = $nonInternalReflectionProphecy->reveal(); + + $realReflectionProphecy = $this->prophesize(ReflectionClass::class); + $realReflectionProphecy->willImplement(Reflection::class); + $realReflectionProphecy + ->isInternal() + ->will( + function ($args): bool { + $x = ''; + } + ) + ; + $internalReflection = $realReflectionProphecy->reveal(); + + $this->classReflectorProphecy + ->reflect('AppKernel') + ->willReturn($nonInternalReflection) + ; + $this->classReflectorProphecy + ->reflect(Argument::any()) + ->willReturn($internalReflection) + ; + + $astLocator = (new BetterReflection())->astLocator(); + + $this->scoper = new PhpScoper( + create_parser(), + new FakeScoper(), + new TraverserFactory( + new ClassReflector( + new AggregateSourceLocator([ + new StringSourceLocator($contents, $astLocator), + new PhpInternalSourceLocator($astLocator), + ]) + ) + ) + ); - $actual = $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist, $whitelister); + $actual = $this->scoper->scope($filePath, $contents, $prefix, $patchers, $whitelist); $titleSeparator = str_repeat( '=', diff --git a/tests/Scoper/TraverserFactoryTest.php b/tests/Scoper/TraverserFactoryTest.php index 2085bda6..b111ed56 100644 --- a/tests/Scoper/TraverserFactoryTest.php +++ b/tests/Scoper/TraverserFactoryTest.php @@ -14,9 +14,8 @@ namespace Humbug\PhpScoper\Scoper; -use Humbug\PhpScoper\Scoper; use PHPUnit\Framework\TestCase; -use function Humbug\PhpScoper\create_fake_whitelister; +use Roave\BetterReflection\BetterReflection; /** * @covers \Humbug\PhpScoper\Scoper\TraverserFactory @@ -29,12 +28,12 @@ public function test_creates_a_new_traverser_at_each_call() $whitelist = ['Foo']; - $whitelister = create_fake_whitelister(); + $classReflector = (new BetterReflection())->classReflector(); - $traverserFactory = new TraverserFactory(); + $traverserFactory = new TraverserFactory($classReflector); - $firstTraverser = $traverserFactory->create($prefix, $whitelist, $whitelister); - $secondTraverser = $traverserFactory->create($prefix, $whitelist, $whitelister); + $firstTraverser = $traverserFactory->create($prefix, $whitelist); + $secondTraverser = $traverserFactory->create($prefix, $whitelist); $this->assertNotSame($firstTraverser, $secondTraverser); } diff --git a/tests/functions.php b/tests/functions.php index 6993bd40..9b52a4cb 100644 --- a/tests/functions.php +++ b/tests/functions.php @@ -72,10 +72,3 @@ function create_fake_patcher(): Closure throw new LogicException('Did not expect to be called'); }; } - -function create_fake_whitelister(): Closure -{ - return function () { - throw new LogicException('Did not expect to be called'); - }; -} From 9e0cbbeeed206c2a9578b09c03a3c3e67af9056d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 15 Feb 2018 19:07:37 +0000 Subject: [PATCH 2/5] Address comments --- .TODO | 17 ------ src/NodeVisitor/NameStmtPrefixer.php | 4 -- src/NodeVisitor/NamespaceStmtPrefixer.php | 63 +---------------------- src/Scoper/TraverserFactory.php | 2 +- tests/Scoper/PhpScoperTest.php | 26 ---------- 5 files changed, 3 insertions(+), 109 deletions(-) delete mode 100644 .TODO diff --git a/.TODO b/.TODO deleted file mode 100644 index dedd9e8b..00000000 --- a/.TODO +++ /dev/null @@ -1,17 +0,0 @@ -Right now whitelisted class works as follow: - -- They are prefixed but a class_alias statement is appended to their declaration with the old name -- Whitelisted class are dumped in a `scoper-autoload.php` autoloader which the users can use. This way, the class - aliases will be triggered at that time - -Right now the use statements of whitelisted class are not prefixed. IMO this should not matter and we could prefix -them still. This would reduce the need of custom code to treat them differently. - - - - ------ - - -Prefix the return types of functions & methods; see func-declaration/global.php -Prefix functions if they are not internal diff --git a/src/NodeVisitor/NameStmtPrefixer.php b/src/NodeVisitor/NameStmtPrefixer.php index 129fecde..1d72c475 100644 --- a/src/NodeVisitor/NameStmtPrefixer.php +++ b/src/NodeVisitor/NameStmtPrefixer.php @@ -51,24 +51,20 @@ final class NameStmtPrefixer extends NodeVisitorAbstract ]; private $prefix; - private $whitelist; private $nameResolver; private $classReflector; /** * @param string $prefix - * @param string[] $whitelist * @param FullyQualifiedNameResolver $nameResolver * @param ClassReflector $classReflector */ public function __construct( string $prefix, - array $whitelist, FullyQualifiedNameResolver $nameResolver, ClassReflector $classReflector ) { $this->prefix = $prefix; - //$this->whitelist = $whitelist; $this->nameResolver = $nameResolver; $this->classReflector = $classReflector; } diff --git a/src/NodeVisitor/NamespaceStmtPrefixer.php b/src/NodeVisitor/NamespaceStmtPrefixer.php index 2f0dbe81..0d12404c 100644 --- a/src/NodeVisitor/NamespaceStmtPrefixer.php +++ b/src/NodeVisitor/NamespaceStmtPrefixer.php @@ -40,7 +40,6 @@ final class NamespaceStmtPrefixer extends NodeVisitorAbstract { private $prefix; private $namespaceStatements; - private $hasWhitelistedNode; public function __construct(string $prefix, NamespaceStmtCollection $namespaceStatements) { @@ -48,38 +47,16 @@ public function __construct(string $prefix, NamespaceStmtCollection $namespaceSt $this->namespaceStatements = $namespaceStatements; } -// /** -// * @inheritdoc -// */ -// public function beforeTraverse(array $nodes) -// { -// $this->hasWhitelistedNode = $this->hasWhitelistedNode($nodes); -// } - /** * @inheritdoc */ public function enterNode(Node $node): Node { - $x = ''; - return ($node instanceof Namespace_) ? $this->prefixNamespaceStmt($node) - : $node; + : $node + ; } -// -// /** -// * @inheritdoc -// */ -// public function leaveNode(Node $node) -// { -// $x = ''; -// -// return ( -// 0 === $this->namespaceStatements->count() -// && false === AppendParentNode::hasParent($node) -// ) ? $this->wrapNamespace($node) : $node; -// } private function prefixNamespaceStmt(Namespace_ $namespace): Node { @@ -96,32 +73,6 @@ private function prefixNamespaceStmt(Namespace_ $namespace): Node return $namespace; } - private function wrapNamespace(Node $node): Node - { - return new Namespace_(new Node\Name($this->prefix), [$node]); - if ($this->isWhitelistedNode($node)) { - } - - // Anything else needs to be wrapped with global namespace. - return new Namespace_(null, [$node]); - } - - /** - * @param Node[] $nodes - * - * @return bool - */ - private function hasWhitelistedNode(array $nodes): bool - { - foreach ($nodes as $node) { - if ($this->isWhitelistedNode($node)) { - return true; - } - } - - return false; - } - private function isWhitelistedNode(Node $node) { if (($node instanceof Class_ || $node instanceof Interface_)) { @@ -143,15 +94,5 @@ private function isWhitelistedNode(Node $node) private function shouldPrefixStmt(Namespace_ $namespace): bool { return null === $namespace->name || (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()); - - if (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()) { - return true; - } - -// if (null === $namespace->name && $this->hasWhitelistedNode([$namespace])) { -// return true; -// } - - return false; } } diff --git a/src/Scoper/TraverserFactory.php b/src/Scoper/TraverserFactory.php index cc77c3c7..a03c1dd4 100644 --- a/src/Scoper/TraverserFactory.php +++ b/src/Scoper/TraverserFactory.php @@ -64,7 +64,7 @@ public function create(string $prefix, array $whitelist): NodeTraverserInterface $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements)); $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $this->classReflector)); - $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $nameResolver, $this->classReflector)); + $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $nameResolver, $this->classReflector)); $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $this->classReflector)); $traverser->addVisitor(new NodeVisitor\WhitelistedClassAppender($whitelist)); diff --git a/tests/Scoper/PhpScoperTest.php b/tests/Scoper/PhpScoperTest.php index e7be0e98..7d33b2b8 100644 --- a/tests/Scoper/PhpScoperTest.php +++ b/tests/Scoper/PhpScoperTest.php @@ -420,32 +420,6 @@ public function test_can_scope_valid_files(string $spec, string $contents, strin $patchers = [create_fake_patcher()]; - $nonInternalReflectionProphecy = $this->prophesize(ReflectionClass::class); - $nonInternalReflectionProphecy->willImplement(Reflection::class); - $nonInternalReflectionProphecy->isInternal()->willReturn(false); - $nonInternalReflection = $nonInternalReflectionProphecy->reveal(); - - $realReflectionProphecy = $this->prophesize(ReflectionClass::class); - $realReflectionProphecy->willImplement(Reflection::class); - $realReflectionProphecy - ->isInternal() - ->will( - function ($args): bool { - $x = ''; - } - ) - ; - $internalReflection = $realReflectionProphecy->reveal(); - - $this->classReflectorProphecy - ->reflect('AppKernel') - ->willReturn($nonInternalReflection) - ; - $this->classReflectorProphecy - ->reflect(Argument::any()) - ->willReturn($internalReflection) - ; - $astLocator = (new BetterReflection())->astLocator(); $this->scoper = new PhpScoper( From 5479356d6fa1e4ee24c4e96971ef97c3efd18180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 15 Feb 2018 20:21:14 +0000 Subject: [PATCH 3/5] Address comments and fix usage with class not found --- TODO-roave | 112 -------------------- specs/new/global-scope-single-part.php | 23 ++++ specs/use/use-class.php | 22 ++++ src/NodeVisitor/NameStmtPrefixer.php | 17 +-- src/NodeVisitor/StringScalarPrefixer.php | 11 +- src/NodeVisitor/UseStmt/UseStmtPrefixer.php | 11 +- src/Reflector.php | 27 +++++ src/Scoper/TraverserFactory.php | 13 +-- src/functions.php | 11 +- tests/Scoper/PhpScoperTest.php | 40 +++---- 10 files changed, 127 insertions(+), 160 deletions(-) delete mode 100644 TODO-roave create mode 100644 src/Reflector.php diff --git a/TODO-roave b/TODO-roave deleted file mode 100644 index 9d2324b6..00000000 --- a/TODO-roave +++ /dev/null @@ -1,112 +0,0 @@ -// vendor/roave/better-reflection/src/SourceLocator/Ast/Locator.php - -findReflectionsInTree = new FindReflectionsInTree(new NodeToReflection()); - - $this->parser = $parser; - } - - /** - * @param Reflector $reflector - * @param LocatedSource $locatedSource - * @param Identifier $identifier - * @return Reflection - * @throws \Roave\BetterReflection\Reflector\Exception\IdentifierNotFound - * @throws Exception\ParseToAstFailure - */ - public function findReflection( - Reflector $reflector, - LocatedSource $locatedSource, - Identifier $identifier - ) : Reflection { - return $this->findInArray( - $this->findReflectionsOfType( - $reflector, - $locatedSource, - $identifier->getType() - ), - $identifier - ); - } - - /** - * Get an array of reflections found in some code. - * - * @param Reflector $reflector - * @param LocatedSource $locatedSource - * @param IdentifierType $identifierType - * @return \Roave\BetterReflection\Reflection\Reflection[] - * @throws Exception\ParseToAstFailure - */ - public function findReflectionsOfType( - Reflector $reflector, - LocatedSource $locatedSource, - IdentifierType $identifierType - ) : array { - try { - return $this->findReflectionsInTree->__invoke( - $reflector, - $this->parser->parse($locatedSource->getSource()), - $identifierType, - $locatedSource - ); - } catch (Throwable $exception) { - throw Exception\ParseToAstFailure::fromLocatedSource($locatedSource, $exception); - } - } - - /** - * Given an array of Reflections, try to find the identifier. - * - * @param Reflection[] $reflections - * @param Identifier $identifier - * @return Reflection - * @throws \Roave\BetterReflection\Reflector\Exception\IdentifierNotFound - */ - private function findInArray(array $reflections, Identifier $identifier) : Reflection - { - // MODIF HERE - $identifierName = strtolower($identifier->getName()); - - foreach ($reflections as $reflection) { - if (strtolower($reflection->getName()) === $identifierName) { - // -MODIF HERE - return $reflection; - } - } - - throw IdentifierNotFound::fromIdentifier($identifier); - } -} diff --git a/specs/new/global-scope-single-part.php b/specs/new/global-scope-single-part.php index 79ff2a35..15a37047 100644 --- a/specs/new/global-scope-single-part.php +++ b/specs/new/global-scope-single-part.php @@ -93,6 +93,29 @@ class Foo } new \Humbug\Foo(); +PHP + ], + + [ + 'spec' => <<<'SPEC' +New statement call of an unknown class belonging to the global namespace: +- wrap everything in a prefixed namespace +- prefix the class +- transform the class in a FQCN call +prefix it +SPEC + , + 'payload' => <<<'PHP' + <<<'SPEC' +Use statement of a non existent class belonging to the global scope: +- wrap the statement in a prefixed namespace +- do not prefix the use statement +SPEC + , + 'payload' => <<<'PHP' +prefix = $prefix; $this->nameResolver = $nameResolver; - $this->classReflector = $classReflector; + $this->reflector = $reflector; } /** @@ -122,7 +123,7 @@ private function prefixName(Name $name): Node // Check if the class can be prefixed if (false === ($parentNode instanceof ConstFetch || $parentNode instanceof FuncCall)) { - if ($this->classReflector->reflect($resolvedName->toString())->isInternal()) { + if ($this->reflector->isClassInternal($resolvedName->toString())) { return $resolvedName; } } @@ -141,6 +142,10 @@ private function prefixName(Name $name): Node return $resolvedName; } - return FullyQualified::concat($this->prefix, $resolvedName->toString(), $resolvedName->getAttributes()); + return FullyQualified::concat( + $this->prefix, + $resolvedName->toString(), + $resolvedName->getAttributes() + ); } } diff --git a/src/NodeVisitor/StringScalarPrefixer.php b/src/NodeVisitor/StringScalarPrefixer.php index 00a864a4..cc392570 100644 --- a/src/NodeVisitor/StringScalarPrefixer.php +++ b/src/NodeVisitor/StringScalarPrefixer.php @@ -14,6 +14,7 @@ namespace Humbug\PhpScoper\NodeVisitor; +use Humbug\PhpScoper\Reflector; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; @@ -41,24 +42,24 @@ final class StringScalarPrefixer extends NodeVisitorAbstract private $prefix; private $whitelistedFunctions; private $whitelist; - private $classReflector; + private $reflector; /** * @param string $prefix * @param string[] $whitelistedFunctions * @param string[] $whitelist - * @param ClassReflector $classReflector + * @param Reflector $reflector */ public function __construct( string $prefix, array $whitelistedFunctions, array $whitelist, - ClassReflector $classReflector + Reflector $reflector ) { $this->prefix = $prefix; $this->whitelistedFunctions = $whitelistedFunctions; $this->whitelist = $whitelist; - $this->classReflector = $classReflector; + $this->reflector = $reflector; } /** @@ -105,7 +106,7 @@ private function prefixStringScalar(String_ $string): Node $newStringName = $stringName; // Check if the class can be prefixed: class from the global namespace } elseif (1 === count($stringName->parts) - && false === $this->classReflector->reflect($stringName->toString())->isInternal() + && false === $this->reflector->isClassInternal($stringName->toString()) ) { $newStringName = $stringName; // Check if the class can be prefixed: regular class diff --git a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php index 54260fbf..281cc450 100644 --- a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php +++ b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php @@ -15,6 +15,7 @@ namespace Humbug\PhpScoper\NodeVisitor\UseStmt; use Humbug\PhpScoper\NodeVisitor\AppendParentNode; +use Humbug\PhpScoper\Reflector; use PhpParser\Node; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Use_; @@ -31,18 +32,18 @@ final class UseStmtPrefixer extends NodeVisitorAbstract { private $prefix; private $whitelist; - private $classReflector; + private $reflector; /** * @param string $prefix * @param string[] $whitelist - * @param ClassReflector $classReflector + * @param Reflector $reflector */ - public function __construct(string $prefix, array $whitelist, ClassReflector $classReflector) + public function __construct(string $prefix, array $whitelist, Reflector $reflector) { $this->prefix = $prefix; $this->whitelist = $whitelist; - $this->classReflector = $classReflector; + $this->reflector = $reflector; } /** @@ -74,7 +75,7 @@ private function shouldPrefixUseStmt(UseUse $use): bool if (1 === count($use->name->parts)) { return Use_::TYPE_NORMAL !== $useType - || false === $this->classReflector->reflect($use->name->getFirst())->isInternal() + || false === $this->reflector->isClassInternal($use->name->getFirst()) ; } diff --git a/src/Reflector.php b/src/Reflector.php new file mode 100644 index 00000000..a9e9149e --- /dev/null +++ b/src/Reflector.php @@ -0,0 +1,27 @@ +classReflector = $classReflector; + } + + public function isClassInternal(string $name): bool + { + try { + return $this->classReflector->reflect($name)->isInternal(); + } catch (IdentifierNotFound $exception) { + return false; + } + } +} \ No newline at end of file diff --git a/src/Scoper/TraverserFactory.php b/src/Scoper/TraverserFactory.php index a03c1dd4..10fcca1d 100644 --- a/src/Scoper/TraverserFactory.php +++ b/src/Scoper/TraverserFactory.php @@ -19,6 +19,7 @@ use Humbug\PhpScoper\NodeVisitor\Collection\NamespaceStmtCollection; use Humbug\PhpScoper\NodeVisitor\Collection\UseStmtCollection; use Humbug\PhpScoper\NodeVisitor\Resolver\FullyQualifiedNameResolver; +use Humbug\PhpScoper\Reflector; use PhpParser\NodeTraverserInterface; use Roave\BetterReflection\Reflector\ClassReflector; @@ -35,11 +36,11 @@ class TraverserFactory 'interface_exists', ]; - private $classReflector; + private $reflector; - public function __construct(ClassReflector $classReflector) + public function __construct(Reflector $reflector) { - $this->classReflector = $classReflector; + $this->reflector = $reflector; } /** @@ -62,10 +63,10 @@ public function create(string $prefix, array $whitelist): NodeTraverserInterface $traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements)); $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements)); - $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $this->classReflector)); + $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $this->reflector)); - $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $nameResolver, $this->classReflector)); - $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $this->classReflector)); + $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $nameResolver, $this->reflector)); + $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $this->reflector)); $traverser->addVisitor(new NodeVisitor\WhitelistedClassAppender($whitelist)); diff --git a/src/functions.php b/src/functions.php index 614c7ae1..864c0115 100644 --- a/src/functions.php +++ b/src/functions.php @@ -97,9 +97,7 @@ function create_scoper(): Scoper new NullScoper() ) ), - new TraverserFactory( - (new BetterReflection())->classReflector() - ) + new TraverserFactory(create_reflector()) ) ); } @@ -112,6 +110,13 @@ function create_parser(): Parser return (new ParserFactory())->create(ParserFactory::ONLY_PHP7); } +function create_reflector(): Reflector +{ + return new Reflector( + (new BetterReflection())->classReflector() + ); +} + /** * @param string[] $paths Absolute paths * diff --git a/tests/Scoper/PhpScoperTest.php b/tests/Scoper/PhpScoperTest.php index 7d33b2b8..4dc97dd1 100644 --- a/tests/Scoper/PhpScoperTest.php +++ b/tests/Scoper/PhpScoperTest.php @@ -16,6 +16,7 @@ use Generator; use Humbug\PhpScoper\PhpParser\FakeParser; +use Humbug\PhpScoper\Reflector; use Humbug\PhpScoper\Scoper; use LogicException; use PhpParser\Error as PhpParserError; @@ -26,7 +27,6 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use ReflectionClass; -use Reflector; use Roave\BetterReflection\BetterReflection; use Roave\BetterReflection\Reflection\Reflection; use Roave\BetterReflection\Reflector\ClassReflector; @@ -49,16 +49,6 @@ class PhpScoperTest extends TestCase */ private $scoper; - /** - * @var string - */ - private $cwd; - - /** - * @var string - */ - private $tmp; - /** * @var Scoper|ObjectProphecy */ @@ -133,7 +123,9 @@ public function setUp() create_parser(), new FakeScoper(), new TraverserFactory( - $this->classReflector + new Reflector( + $this->classReflector + ) ) ); } @@ -146,7 +138,7 @@ public function test_is_a_Scoper() public function test_can_scope_a_PHP_file() { $prefix = 'Humbug'; - $filePath = escape_path($this->tmp.'/file.php'); + $filePath = 'file.php'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; @@ -206,7 +198,7 @@ public function test_does_not_scope_file_if_is_not_a_PHP_file() public function test_can_scope_a_PHP_file_with_the_wrong_extension() { $prefix = 'Humbug'; - $filePath = escape_path($this->tmp.'/file'); + $filePath = 'file'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; @@ -234,7 +226,7 @@ public function test_can_scope_a_PHP_file_with_the_wrong_extension() public function test_can_scope_PHP_binary_files() { $prefix = 'Humbug'; - $filePath = escape_path($this->tmp.'/hello'); + $filePath = 'hello'; $patchers = [create_fake_patcher()]; $whitelist = ['Foo']; @@ -263,7 +255,7 @@ public function test_does_not_scope_a_non_PHP_binary_files() { $prefix = 'Humbug'; - $filePath = escape_path($this->tmp.'/hello'); + $filePath = 'hello'; $patchers = [create_fake_patcher()]; @@ -303,7 +295,7 @@ public function test_does_not_scope_a_non_PHP_binary_files() public function test_cannot_scope_an_invalid_PHP_file() { - $filePath = escape_path($this->tmp.'/invalid-file.php'); + $filePath = 'invalid-file.php'; $contents = <<<'PHP' tmp.'/file.php'); + $filePath = 'file.php'; $patchers = [create_fake_patcher()]; @@ -426,11 +418,13 @@ public function test_can_scope_valid_files(string $spec, string $contents, strin create_parser(), new FakeScoper(), new TraverserFactory( - new ClassReflector( - new AggregateSourceLocator([ - new StringSourceLocator($contents, $astLocator), - new PhpInternalSourceLocator($astLocator), - ]) + new Reflector( + new ClassReflector( + new AggregateSourceLocator([ + new StringSourceLocator($contents, $astLocator), + new PhpInternalSourceLocator($astLocator), + ]) + ) ) ) ); From 02ef70e4e765d33728348b900ad23ea359799513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 15 Feb 2018 22:00:43 +0000 Subject: [PATCH 4/5] Fix e2e tests --- Makefile | 10 +++++----- src/NodeTraverser.php | 3 --- src/functions.php | 17 ++++++++++++++++- tests/Scoper/TraverserFactoryTest.php | 5 ++++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 479d1f03..7ae8efaa 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ e2e: e2e_004 e2e_005 e2e_011 e2e_013 e2e_014 e2e_015 e2e_004: ## Run end-to-end tests for the fixture set 004: source code case e2e_004: bin/php-scoper.phar - php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set004 --output-dir=../../build/set004 --force --no-config --no-interaction + php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set004 --output-dir=../../build/set004 --force --no-config --no-interaction --stop-on-failure composer --working-dir=build/set004 dump-autoload php -d zend.enable_gc=0 -d phar.readonly=0 $(BOX) build -c build/set004/box.json.dist @@ -71,7 +71,7 @@ e2e_004: bin/php-scoper.phar e2e_005: ## Run end-to-end tests for the fixture set 005: third-party code case e2e_005: bin/php-scoper.phar fixtures/set005/vendor - php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set005 --output-dir=../../build/set005 --force --no-config --no-interaction + php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set005 --output-dir=../../build/set005 --force --no-config --no-interaction --stop-on-failure composer --working-dir=build/set005 dump-autoload php -d zend.enable_gc=0 -d phar.readonly=0 $(BOX) build -c build/set005/box.json.dist @@ -80,7 +80,7 @@ e2e_005: bin/php-scoper.phar fixtures/set005/vendor e2e_011: ## Run end-to-end tests for the fixture set 011: whitelist case e2e_011: bin/php-scoper.phar fixtures/set011/vendor - php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set011 --output-dir=../../build/set011 --force --no-interaction + php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set011 --output-dir=../../build/set011 --force --no-interaction --stop-on-failure cp -R fixtures/set011/tests build/set011/ composer --working-dir=build/set011 dump-autoload php -d zend.enable_gc=0 -d phar.readonly=0 $(BOX) build -c build/set011/box.json.dist @@ -97,7 +97,7 @@ e2e_013: bin/php-scoper.phar e2e_014: ## Run end-to-end tests for the fixture set 014: source code case with psr-0 e2e_014: bin/php-scoper.phar - php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set014 --output-dir=../../build/set014 --force --no-config --no-interaction + php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set014 --output-dir=../../build/set014 --force --no-config --no-interaction --stop-on-failure composer --working-dir=build/set014 dump-autoload php -d zend.enable_gc=0 -d phar.readonly=0 $(BOX) build -c build/set014/box.json.dist @@ -106,7 +106,7 @@ e2e_014: bin/php-scoper.phar e2e_015: ## Run end-to-end tests for the fixture set 015: third-party code case with psr-0 e2e_015: bin/php-scoper.phar fixtures/set015/vendor - php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set015 --output-dir=../../build/set015 --force --no-config --no-interaction + php -d zend.enable_gc=0 $(PHPSCOPER) add-prefix --working-dir=fixtures/set015 --output-dir=../../build/set015 --force --no-config --no-interaction --stop-on-failure composer --working-dir=build/set015 dump-autoload php -d zend.enable_gc=0 -d phar.readonly=0 $(BOX) build -c build/set015/box.json.dist diff --git a/src/NodeTraverser.php b/src/NodeTraverser.php index d5f45133..49e2c7d0 100644 --- a/src/NodeTraverser.php +++ b/src/NodeTraverser.php @@ -7,9 +7,6 @@ // TODO: re-organise the classes location as this no longer makes sense. Not done here to try to keep the diff humanly // readable. -use function array_slice; -use function array_splice; -use function current; use PhpParser\Node; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Declare_; diff --git a/src/functions.php b/src/functions.php index 864c0115..3784cfa6 100644 --- a/src/functions.php +++ b/src/functions.php @@ -35,6 +35,14 @@ use PhpParser\Parser; use PhpParser\ParserFactory; use Roave\BetterReflection\BetterReflection; +use Roave\BetterReflection\Reflector\ClassReflector; +use Roave\BetterReflection\SourceLocator\Ast\Locator; +use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\ComposerSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\EvaledCodeSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\MemoizingSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; use Symfony\Component\Console\Application as SymfonyApplication; use Symfony\Component\Filesystem\Filesystem; @@ -112,8 +120,15 @@ function create_parser(): Parser function create_reflector(): Reflector { + $phpParser = create_parser(); + $astLocator = new Locator($phpParser); + return new Reflector( - (new BetterReflection())->classReflector() + new ClassReflector( + new MemoizingSourceLocator( + new PhpInternalSourceLocator($astLocator) + ) + ) ); } diff --git a/tests/Scoper/TraverserFactoryTest.php b/tests/Scoper/TraverserFactoryTest.php index b111ed56..0ff20fec 100644 --- a/tests/Scoper/TraverserFactoryTest.php +++ b/tests/Scoper/TraverserFactoryTest.php @@ -14,6 +14,7 @@ namespace Humbug\PhpScoper\Scoper; +use Humbug\PhpScoper\Reflector; use PHPUnit\Framework\TestCase; use Roave\BetterReflection\BetterReflection; @@ -28,7 +29,9 @@ public function test_creates_a_new_traverser_at_each_call() $whitelist = ['Foo']; - $classReflector = (new BetterReflection())->classReflector(); + $classReflector = new Reflector( + (new BetterReflection())->classReflector() + ); $traverserFactory = new TraverserFactory($classReflector); From d3e9c748bbd38384d07e5d5892a7fc32b9095ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Fri, 16 Feb 2018 00:15:20 +0000 Subject: [PATCH 5/5] Fix CS --- src/Console/Command/AddPrefixCommand.php | 1 - src/NodeTraverser.php | 14 +++++++++++--- src/NodeVisitor/NameStmtPrefixer.php | 3 +-- src/NodeVisitor/StringScalarPrefixer.php | 7 +++---- src/NodeVisitor/UseStmt/UseStmtPrefixer.php | 7 ++----- src/Reflector.php | 12 +++++++++++- src/Scoper.php | 2 +- src/Scoper/TraverserFactory.php | 3 +-- src/functions.php | 5 ----- tests/Console/Command/AddPrefixCommandTest.php | 1 - tests/Scoper/NullScoperTest.php | 1 - tests/Scoper/PhpScoperTest.php | 3 --- 12 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/Console/Command/AddPrefixCommand.php b/src/Console/Command/AddPrefixCommand.php index 794d466c..8ece0d55 100644 --- a/src/Console/Command/AddPrefixCommand.php +++ b/src/Console/Command/AddPrefixCommand.php @@ -14,7 +14,6 @@ namespace Humbug\PhpScoper\Console\Command; -use Closure; use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator; use Humbug\PhpScoper\Console\Configuration; use Humbug\PhpScoper\Logger\ConsoleLogger; diff --git a/src/NodeTraverser.php b/src/NodeTraverser.php index 49e2c7d0..cab899d1 100644 --- a/src/NodeTraverser.php +++ b/src/NodeTraverser.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the humbug/php-scoper package. + * + * Copyright (c) 2017 Théo FIDRY , + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace Humbug\PhpScoper; @@ -28,7 +37,6 @@ public function __construct(string $prefix) $this->prefix = $prefix; } - /** * @inheritdoc */ @@ -41,7 +49,7 @@ public function traverse(array $nodes) } /** - * Wrap the statements in a namespace when necessary: + * Wrap the statements in a namespace when necessary:. * * ```php * #!/usr/bin/env php @@ -157,4 +165,4 @@ function (UseUse $use) use ($node): Use_ { $node->uses ); } -} \ No newline at end of file +} diff --git a/src/NodeVisitor/NameStmtPrefixer.php b/src/NodeVisitor/NameStmtPrefixer.php index 839bad5d..b0cf1252 100644 --- a/src/NodeVisitor/NameStmtPrefixer.php +++ b/src/NodeVisitor/NameStmtPrefixer.php @@ -28,7 +28,6 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Interface_; use PhpParser\NodeVisitorAbstract; -use Roave\BetterReflection\Reflector\ClassReflector; /** * ``` @@ -58,7 +57,7 @@ final class NameStmtPrefixer extends NodeVisitorAbstract /** * @param string $prefix * @param FullyQualifiedNameResolver $nameResolver - * @param Reflector $reflector + * @param Reflector $reflector */ public function __construct( string $prefix, diff --git a/src/NodeVisitor/StringScalarPrefixer.php b/src/NodeVisitor/StringScalarPrefixer.php index cc392570..66b0fc82 100644 --- a/src/NodeVisitor/StringScalarPrefixer.php +++ b/src/NodeVisitor/StringScalarPrefixer.php @@ -22,7 +22,6 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\String_; use PhpParser\NodeVisitorAbstract; -use Roave\BetterReflection\Reflector\ClassReflector; /** * Prefixes the string scalar values. @@ -45,9 +44,9 @@ final class StringScalarPrefixer extends NodeVisitorAbstract private $reflector; /** - * @param string $prefix - * @param string[] $whitelistedFunctions - * @param string[] $whitelist + * @param string $prefix + * @param string[] $whitelistedFunctions + * @param string[] $whitelist * @param Reflector $reflector */ public function __construct( diff --git a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php index 281cc450..3b4eecee 100644 --- a/src/NodeVisitor/UseStmt/UseStmtPrefixer.php +++ b/src/NodeVisitor/UseStmt/UseStmtPrefixer.php @@ -21,9 +21,6 @@ use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\UseUse; use PhpParser\NodeVisitorAbstract; -use Roave\BetterReflection\BetterReflection; -use Roave\BetterReflection\Reflection\ReflectionClass; -use Roave\BetterReflection\Reflector\ClassReflector; /** * Prefixes the use statements. @@ -35,8 +32,8 @@ final class UseStmtPrefixer extends NodeVisitorAbstract private $reflector; /** - * @param string $prefix - * @param string[] $whitelist + * @param string $prefix + * @param string[] $whitelist * @param Reflector $reflector */ public function __construct(string $prefix, array $whitelist, Reflector $reflector) diff --git a/src/Reflector.php b/src/Reflector.php index a9e9149e..45073d50 100644 --- a/src/Reflector.php +++ b/src/Reflector.php @@ -2,6 +2,16 @@ declare(strict_types=1); +/* + * This file is part of the humbug/php-scoper package. + * + * Copyright (c) 2017 Théo FIDRY , + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Humbug\PhpScoper; use Roave\BetterReflection\Reflector\ClassReflector; @@ -24,4 +34,4 @@ public function isClassInternal(string $name): bool return false; } } -} \ No newline at end of file +} diff --git a/src/Scoper.php b/src/Scoper.php index f4527547..d468e898 100644 --- a/src/Scoper.php +++ b/src/Scoper.php @@ -22,7 +22,7 @@ interface Scoper * Scope AKA. apply the given prefix to the file in the appropriate way. * * @param string $filePath File to scope - * @param string $contents File contents + * @param string $contents File contents * @param string $prefix Prefix to apply to the file * @param callable[] $patchers * @param string[] $whitelist List of classes to exclude from the scoping. diff --git a/src/Scoper/TraverserFactory.php b/src/Scoper/TraverserFactory.php index 10fcca1d..4bddda63 100644 --- a/src/Scoper/TraverserFactory.php +++ b/src/Scoper/TraverserFactory.php @@ -21,7 +21,6 @@ use Humbug\PhpScoper\NodeVisitor\Resolver\FullyQualifiedNameResolver; use Humbug\PhpScoper\Reflector; use PhpParser\NodeTraverserInterface; -use Roave\BetterReflection\Reflector\ClassReflector; /** * @final @@ -35,7 +34,7 @@ class TraverserFactory 'class_exists', 'interface_exists', ]; - + private $reflector; public function __construct(Reflector $reflector) diff --git a/src/functions.php b/src/functions.php index 3784cfa6..2e31c630 100644 --- a/src/functions.php +++ b/src/functions.php @@ -34,13 +34,8 @@ use PhpParser\Node; use PhpParser\Parser; use PhpParser\ParserFactory; -use Roave\BetterReflection\BetterReflection; use Roave\BetterReflection\Reflector\ClassReflector; use Roave\BetterReflection\SourceLocator\Ast\Locator; -use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; -use Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator; -use Roave\BetterReflection\SourceLocator\Type\ComposerSourceLocator; -use Roave\BetterReflection\SourceLocator\Type\EvaledCodeSourceLocator; use Roave\BetterReflection\SourceLocator\Type\MemoizingSourceLocator; use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; use Symfony\Component\Console\Application as SymfonyApplication; diff --git a/tests/Console/Command/AddPrefixCommandTest.php b/tests/Console/Command/AddPrefixCommandTest.php index f919f733..ff31f92a 100644 --- a/tests/Console/Command/AddPrefixCommandTest.php +++ b/tests/Console/Command/AddPrefixCommandTest.php @@ -14,7 +14,6 @@ namespace Humbug\PhpScoper\Console\Command; -use Closure; use Humbug\PhpScoper\Console\Application; use Humbug\PhpScoper\Scoper; use InvalidArgumentException; diff --git a/tests/Scoper/NullScoperTest.php b/tests/Scoper/NullScoperTest.php index b29f6e3c..95c58ebc 100644 --- a/tests/Scoper/NullScoperTest.php +++ b/tests/Scoper/NullScoperTest.php @@ -17,7 +17,6 @@ use Humbug\PhpScoper\Scoper; use PHPUnit\Framework\TestCase; use function Humbug\PhpScoper\create_fake_patcher; -use function Humbug\PhpScoper\create_fake_whitelister; /** * @covers \Humbug\PhpScoper\Scoper\NullScoper diff --git a/tests/Scoper/PhpScoperTest.php b/tests/Scoper/PhpScoperTest.php index 4dc97dd1..47aab6f3 100644 --- a/tests/Scoper/PhpScoperTest.php +++ b/tests/Scoper/PhpScoperTest.php @@ -26,9 +26,7 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; -use ReflectionClass; use Roave\BetterReflection\BetterReflection; -use Roave\BetterReflection\Reflection\Reflection; use Roave\BetterReflection\Reflector\ClassReflector; use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; @@ -37,7 +35,6 @@ use Throwable; use function Humbug\PhpScoper\create_fake_patcher; use function Humbug\PhpScoper\create_parser; -use function Humbug\PhpScoper\escape_path; class PhpScoperTest extends TestCase {