Add reflection classes to mutators. Do no mutate public method visibility if parent has the same one#67
Conversation
77108bd to
aa9b318
Compare
|
Both humbug/php-scoper#127 and nikic/PHP-Parser#441 are fixed now. |
|
I would try PHP-Scoper a bit later and in a dedicated PR as I'm sure it's gonna be a work on its own. Infection may be fine itself, but guaranteed to find weird cases with PhpSpec & PHPUnit |
great. Will continue working on this PR next week
it shouldn't affect prefixing Infection as I understand since we don't have such dependencies in |
92fb97e to
fea4fd7
Compare
|
@infection/core I think this is ready for review. I've updated initial comment, please see details. If you have some time - could you please check it with some of your project(s)? With prefixed PHAR and as a dependency. Not sure why builds are failing on PHP 7.2 with @theofidry your feedback on PHP-Scoper usage is highly appreciated |
|
@borNfreee I've run Infection from this PR under phpdbg (php7.2) and got the same issue with segfault. Potentially it's an issue with php itself. It'll be nice to submit an issue (if it's an php issue) to the php bug-tracker |
I was able to reproduce it with PHP 7.2.2 today. It fails with Will continue git-bisecting tomorrow. |
theofidry
left a comment
There was a problem hiding this comment.
I left some comments. I'll have to check that in more details as there's significant changes I need to pay more attention to it.
Regarding PHP Scoper I think it's mandatory to have an e2e test with the scoped file if you wanna integrate it. That said maybe this can be done in a separate PR.
Also I didn't see any change on how we are building the PHAR to pick up the scoped files, did I miss something?
scoper.inc.php
Outdated
| // heart contents. | ||
| // | ||
| // For more see: https://github.com/humbug/php-scoper#patchers | ||
| 'patchers' => [ |
There was a problem hiding this comment.
you can remove this entry as you're not using it atm
scoper.inc.php
Outdated
| // (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' => [ |
scoper.inc.php
Outdated
| // that this does not work with functions or constants neither with classes belonging to the global namespace. | ||
| // | ||
| // Fore more see https://github.com/humbug/php-scoper#whitelist | ||
| 'whitelist' => [ |
| CONTENT; | ||
| } | ||
|
|
||
| private function getInterceptorNamespacePrefix() |
There was a problem hiding this comment.
you can add the string return type
| $this->namespace = $node->name; | ||
| } elseif ($node instanceof Stmt\ClassLike) { | ||
| if (null !== $node->name) { | ||
| $node->fullyQualifiedClassName = Name::concat($this->namespace, $node->name); |
There was a problem hiding this comment.
Is this supposed to work for any class? Because if that's the case this won't be enough as you need to account for when an alias is used with the use statement (if any)
There was a problem hiding this comment.
I'm not sure I got it. This visitor's goal is to build FQCL for the current class, not for other imported (via use statement) classes.
When we have:
namespace Infection\Mutator;
use SomeClass;
use Some\OtherClass as Aliased;
class Plus {
}this visitor should build a Infection\Mutator\Plus FQCN and save it to the node to fullyQualifiedClassName property.
It does not need to work with SomeClass, OtherClass, Aliased.
Then we use it to build reflection in ReflectionVisitor:
if ($node instanceof Node\Stmt\ClassLike) {
$this->reflectionClass = new \ReflectionClass($node->fullyQualifiedClassName->toString());
}There was a problem hiding this comment.
Oh right, I had my head too much in php-scoper. Should be alright then :)
|
|
||
| $mutatedCode = $this->mutate($code); | ||
|
|
||
| $expectedMutatedCode = <<<'CODE' |
There was a problem hiding this comment.
I think 'PHP' would be better here as IDEs can pick that up for inspections
|
|
||
| public function test_it_does_not_mutate_if_parent_class_has_same_protected_method() | ||
| { | ||
| $code = file_get_contents(__DIR__ . '/../../Fixtures/Autoloaded/ProtectedVisibility/pv-same-method-parent.php'); |
There was a problem hiding this comment.
instead of having __DIR__ . '/../../Fixtures/Autoloaded[...] all the time everywhere maybe you can introduce a constant in the test case with the base path
I don't know how relevant this is, but maybe we can allow users to define the location of the autoload file in the infection config. Similar to how phpunit does things. |
6549cac to
20ad10d
Compare
|
Finally, rebased on master, ready for review again. Regarding PHPScoper config, I think we will use Theo's one from humbug/php-scoper#172 later (but here PHPParser has been upgraded, so not all patchers are needed) Also, I think we will release 0.8.0 and this PR will be shipped with the next major release as it is not ready yet.
what does it bring for us? |
src/Visitor/ReflectionVisitor.php
Outdated
|
|
||
| public function enterNode(Node $node) | ||
| { | ||
| if ($node instanceof Node\Stmt\ClassLike) { |
There was a problem hiding this comment.
What happens here if we declare an anonymous class within our class?
Will all the nodes after that still be seen as instances of that anonymous class?
There was a problem hiding this comment.
It crashes on anonymous classes in my test cases.
This could be fixed by
public function enterNode(Node $node)
{
if ($node instanceof Stmt\Namespace_) {
$this->namespace = $node->name;
} elseif ($node instanceof Stmt\ClassLike) {
if (null !== $node->name) {
$node->fullyQualifiedClassName = Name::concat($this->namespace, $node->name);
} else {
$node->fullyQualifiedClassName = null;
}
}
}For the FullyQualifiedClassNameVisitor
and
public function enterNode(Node $node)
{
if ($node instanceof Node\Stmt\ClassLike && $node->fullyQualifiedClassName !== null) {
$this->reflectionClass = new \ReflectionClass($node->fullyQualifiedClassName->toString());
}in the ReflectionVisitor
There was a problem hiding this comment.
Thank you @BackEndTea, I've added a test case.
I realize now that it not that usefull for us. Phpunits version is for bootstrapping an application, not just autoloading |
|
Looks good to me. Id just like to see a test for the reflection visitor that checks if the reflection class is correctly set. |
…ibility if parent has the same one Signed-off-by: borNfreee <b0rn@list.ru> User the correct namespace for Interceptor when code is prefixed with random character(s) Signed-off-by: borNfreee <b0rn@list.ru> Rebase and refactor. Add more tests Update PHPParser to 3.1.4 Do not analyze Fixture files by PHPStan as it loads the code and can't parse PHP7.1 syntax. Skip tests on PHP7.0 that requires 7.1+ Do not analyze Fixture files by PHPStan as it loads the code and can't parse PHP7.1 syntax. Skip tests on PHP7.0 that requires 7.1+ Add PHP-Scoper config. Decrease MSI since phpdbg results with the lower number than xdebug Rebase, resolve conflicts Remove empty files with PHP extension. This causes a segfault with phpdbg on PHP 7.2
ef5101a to
ec27507
Compare
| return false; | ||
| } | ||
|
|
||
| if ($this->hasSameProtectedParentMethod($node)) { |
There was a problem hiding this comment.
Just a small thing, but maybe its slightly faster to first check if the node is protected, before checking if it has a protected parent.
And ofcourse the same goes for public
|
Ok, I think it's ready to be merged (this PR also unlocks Profiles implementation). Thanks everyone for the review and help. |
This is an attempt to add native PHP Reflection to Mutator Operators. Fixes #63.
\ReflectionClassinstance for each Node inside functions/methodsinfection.phar. This is needed because now we load the source code of a target project (creating instance of\ReflectionClassloads the target class).BLOCKED by PSR-0 dependencies are not correctly prefixed humbug/php-scoper#127BLOCKED by Correctly determine Type of Node when PHP-Parser's namespaces are prefixed nikic/PHP-Parser#441As a result:
PublicVisibilityandProtectedVisibilitymutators (-130 mutants for infection itself).Please note:
app/bootstrap.php) in order to instantiate\ReflectionClass()instancesHo to prefix:
cd infection composer install --no-dev /path/to/php-scoper/bin/php-scoper add-prefix --output-dir=build-prefixed --force --prefix=Mutant composer dump-autoload --working-dir=build-prefixed/ --classmap-authoritative --no-dev sudo chmod +x build-prefixed/bin/infectionFeedback/review is much appreciated.
UPD: Finally, I was able to prefix Infection lib with PHP-Scoper without errors, got the same MSI/Covered MSI, number of mutations, mutated files with Prefixed PHAR as with the original source code.