Skip to content
This repository was archived by the owner on Jul 12, 2020. It is now read-only.

Commit 1dce279

Browse files
author
Tom Oram
committed
Allow convert local variable to instance variable to work with function arguments
1 parent c951cbf commit 1dce279

File tree

7 files changed

+231
-26
lines changed

7 files changed

+231
-26
lines changed

composer.lock

+20-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

features/convert_local_to_instance_variable.feature

+41
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,44 @@ Feature: Convert Local to Instance Variable
4646
}
4747
"""
4848

49+
Scenario: Convert Argument Variable
50+
Given a PHP File named "src/FooWithArgument.php" with:
51+
"""
52+
<?php
53+
class FooWithArgument
54+
{
55+
public function operation($service)
56+
{
57+
$service->operation();
58+
59+
return $service;
60+
}
61+
}
62+
"""
63+
When I use refactoring "convert-local-to-instance-variable" with:
64+
| arg | value |
65+
| file | src/FooWithArgument.php |
66+
| line | 6 |
67+
| variable | service |
68+
Then the PHP File "src/FooWithArgument.php" should be refactored:
69+
"""
70+
--- a/vfs://project/src/FooWithArgument.php
71+
+++ b/vfs://project/src/FooWithArgument.php
72+
@@ -1,10 +1,14 @@
73+
<?php
74+
class FooWithArgument
75+
{
76+
+ private $service;
77+
+
78+
public function operation($service)
79+
{
80+
- $service->operation();
81+
+ $this->service = $service;
82+
83+
- return $service;
84+
+ $this->service->operation();
85+
+
86+
+ return $this->service;
87+
}
88+
}
89+
"""

src/main/QafooLabs/Refactoring/Adapters/PatchBuilder/PatchBuilder.php

+1-4
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,7 @@ public function changeToken($originalLine, $oldToken, $newToken)
8686
*/
8787
public function appendToLine($originalLine, array $lines)
8888
{
89-
// Why is this one method different to the rest?
90-
$originalLine++;
91-
92-
$this->buffer->insert($this->createLineNumber($originalLine), $lines);
89+
$this->buffer->append($this->createLineNumber($originalLine), $lines);
9390
}
9491

9592
/**

src/main/QafooLabs/Refactoring/Adapters/TokenReflection/StaticCodeAnalysis.php

+12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
use TokenReflection\Broker;
2424
use TokenReflection\Broker\Backend\Memory;
25+
use QafooLabs\Refactoring\Domain\Model\RefactoringException;
2526

2627
class StaticCodeAnalysis extends CodeAnalysis
2728
{
@@ -90,6 +91,17 @@ public function isInsideMethod(File $file, LineRange $range)
9091
return $this->findMatchingMethod($file, $range) !== null;
9192
}
9293

94+
public function getMethod(File $file, LineRange $range)
95+
{
96+
$method = $this->findMatchingMethod($file, $range);
97+
98+
if ($method === null) {
99+
throw RefactoringException::rangeIsNotInsideMethod($range);
100+
}
101+
102+
return $method;
103+
}
104+
93105
/**
94106
* @param File $file
95107
* @return PhpClass[]

src/main/QafooLabs/Refactoring/Application/ConvertLocalToInstanceVariable.php

+61-2
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,24 @@
1313
use QafooLabs\Refactoring\Domain\Services\VariableScanner;
1414
use QafooLabs\Refactoring\Domain\Services\CodeAnalysis;
1515
use QafooLabs\Refactoring\Domain\Services\Editor;
16+
use QafooLabs\Refactoring\Domain\Model\EditingAction\AddAssignment;
1617
use QafooLabs\Refactoring\Domain\Model\EditingAction\AddProperty;
1718
use QafooLabs\Refactoring\Domain\Model\EditingAction\LocalVariableToInstance;
1819

20+
use TokenReflection\ReflectionMethod;
21+
1922
class ConvertLocalToInstanceVariable extends SingleFileRefactoring
2023
{
2124
/**
2225
* @var Variable
2326
*/
2427
private $convertVariable;
2528

29+
/**
30+
* @var ReflectionMethod
31+
*/
32+
private $method;
33+
2634
/**
2735
* @param int $line
2836
*/
@@ -32,11 +40,19 @@ public function refactor(File $file, $line, Variable $convertVariable)
3240
$this->line = $line;
3341
$this->convertVariable = $convertVariable;
3442

43+
$this->method = $this->codeAnalysis->getMethod($this->file, LineRange::fromSingleLine($line));
44+
3545
$this->assertIsInsideMethod();
3646

3747
$this->startEditingSession();
48+
3849
$this->addProperty();
3950
$this->convertVariablesToInstanceVariables();
51+
52+
if ($this->variableIsMethodParameter()) {
53+
$this->assignArgumentVariableToInstanceVariable();
54+
}
55+
4056
$this->completeEditingSession();
4157
}
4258

@@ -51,12 +67,55 @@ private function addProperty()
5167

5268
private function convertVariablesToInstanceVariables()
5369
{
70+
$range = $this->getMethodBodyRange();
71+
5472
$definedVariables = $this->getDefinedVariables();
5573

74+
$this->assertVariableIsDefiniedInScope($range, $definedVariables);
75+
76+
$this->session->addEdit(new LocalVariableToInstance($definedVariables, $this->convertVariable));
77+
}
78+
79+
private function variableIsMethodParameter()
80+
{
81+
return in_array($this->convertVariable->getName(), array_map(function ($parameter) {
82+
return $parameter->getName();
83+
}, $this->method->getParameters()));
84+
}
85+
86+
private function assignArgumentVariableToInstanceVariable()
87+
{
88+
$instanceVariable = $this->convertVariable->convertToInstance();
89+
90+
$line = $this->method->getStartLine() + 1;
91+
92+
// The +1 assumes that the function definition is followed by a newline with the
93+
// opening brace. Ideally this needs to be detected.
94+
$this->session->addEdit(new AddAssignment($line, $instanceVariable, $this->convertVariable->getToken()));
95+
}
96+
97+
protected function getDefinedVariables()
98+
{
99+
$selectedMethodLineRange = $this->getMethodBodyRange();
100+
101+
$definedVariables = $this->variableScanner->scanForVariables(
102+
$this->file, $selectedMethodLineRange
103+
);
104+
105+
return $definedVariables;
106+
}
107+
108+
private function getMethodBodyRange()
109+
{
110+
$methodLineRange = $this->codeAnalysis->findMethodRange($this->file, LineRange::fromSingleLine($this->line));
111+
112+
return LineRange::fromLines($methodLineRange->getStart() + 1, $methodLineRange->getEnd());
113+
}
114+
115+
private function assertVariableIsDefiniedInScope(LineRange $selectedMethodLineRange, DefinedVariables $definedVariables)
116+
{
56117
if ( ! $definedVariables->contains($this->convertVariable)) {
57118
throw RefactoringException::variableNotInRange($this->convertVariable, $selectedMethodLineRange);
58119
}
59-
60-
$this->session->addEdit(new LocalVariableToInstance($definedVariables, $this->convertVariable));
61120
}
62121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
4+
5+
use QafooLabs\Refactoring\Domain\Model\EditingAction;
6+
use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
7+
use QafooLabs\Refactoring\Domain\Model\Variable;
8+
9+
class AddAssignment implements EditingAction
10+
{
11+
/**
12+
* @var int
13+
*/
14+
private $line;
15+
16+
/**
17+
* @var Variable
18+
*/
19+
private $lhs;
20+
21+
/**
22+
* @var string
23+
*/
24+
private $rhs;
25+
26+
/**
27+
* @param int $line
28+
* @param string $rhs
29+
*/
30+
public function __construct($line, Variable $lhs, $rhs)
31+
{
32+
$this->line = $line;
33+
$this->lhs = $lhs;
34+
$this->rhs = $rhs;
35+
}
36+
37+
public function performEdit(EditorBuffer $buffer)
38+
{
39+
$buffer->append($this->line, array(
40+
' ' . $this->lhs->getToken() . ' = ' . $this->rhs . ';',
41+
''
42+
));
43+
}
44+
}

0 commit comments

Comments
 (0)