Skip to content

Commit 4f33c6e

Browse files
committed
way to get visibility and state of method
Conflicts: src/Hal/Component/OOP/Reflected/ReflectedMethod.php
1 parent 51e50ed commit 4f33c6e

File tree

15 files changed

+266
-1
lines changed

15 files changed

+266
-1
lines changed

src/Hal/Component/OOP/Extractor/MethodExtractor.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,21 @@ public function __construct(Searcher $searcher)
4747
*/
4848
public function extract(&$n, TokenCollection $tokens)
4949
{
50+
$start = $n;
51+
5052
$declaration = $this->searcher->getUnder(array(')'), $n, $tokens);
5153
if(!preg_match('!function\s+(.*)\(\s*(.*)!is', $declaration, $matches)) {
5254
throw new \Exception(sprintf("Closure detected instead of method\nDetails:\n%s", $declaration));
5355
}
5456
list(, $name, $args) = $matches;
5557
$method = new ReflectedMethod($name);
5658

59+
// visibility
60+
$this->extractVisibility($method, $p = $start, $tokens); // please keep "p = start"
61+
62+
// state
63+
$this->extractState($method, $p = $start, $tokens); // please keep "p = start"
64+
5765
$arguments = preg_split('!\s*,\s*!m', $args);
5866
foreach($arguments as $argDecl) {
5967

@@ -80,6 +88,8 @@ public function extract(&$n, TokenCollection $tokens)
8088
$method->pushArgument($argument);
8189
}
8290

91+
92+
8393
//
8494
// Body
8595
$this->extractContent($method, $n, $tokens);
@@ -100,9 +110,51 @@ public function extract(&$n, TokenCollection $tokens)
100110
// usage
101111
$this->extractUsage($method);
102112

113+
114+
103115
return $method;
104116
}
105117

118+
/**
119+
* Extracts visibility
120+
*
121+
* @param ReflectedMethod $method
122+
* @param $n
123+
* @param TokenCollection $tokens
124+
* @return $this
125+
*/
126+
public function extractVisibility(ReflectedMethod $method, $n, TokenCollection $tokens) {
127+
switch(true) {
128+
case $this->searcher->isPrecededBy(T_PRIVATE, $n, $tokens, 4):
129+
$visibility = ReflectedMethod::VISIBILITY_PRIVATE;
130+
break;
131+
case $this->searcher->isPrecededBy(T_PROTECTED, $n, $tokens, 4):
132+
$visibility = ReflectedMethod::VISIBILITY_PROTECTED;
133+
break;
134+
case $this->searcher->isPrecededBy(T_PUBLIC, $n, $tokens, 4):
135+
default:
136+
$visibility = ReflectedMethod::VISIBILITY_PUBLIC;
137+
break;
138+
}
139+
$method->setVisibility($visibility);
140+
return $this;
141+
}
142+
143+
/**
144+
* Extracts state
145+
*
146+
* @param ReflectedMethod $method
147+
* @param $n
148+
* @param TokenCollection $tokens
149+
* @return $this
150+
*/
151+
public function extractState(ReflectedMethod $method, $n, TokenCollection $tokens) {
152+
if($this->searcher->isPrecededBy(T_STATIC, $n, $tokens, 4)) {
153+
$method->setState(ReflectedMethod::STATE_STATIC);
154+
}
155+
return $this;
156+
}
157+
106158
/**
107159
* Extracts content of method
108160
*

src/Hal/Component/OOP/Extractor/Searcher.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,33 @@ public function getClassNamePosition(TokenCollection $tokens)
125125
}
126126
return null;
127127
}
128+
129+
public function getPositionOfPrevious($tokenType, $n, TokenCollection $tokens) {
130+
for($i = $n; $i > 0; $i--) {
131+
if($tokenType == $tokens->get($i)->getType()) {
132+
return $i;
133+
}
134+
}
135+
return null;
136+
}
137+
138+
public function getPositionOfNext($tokenType, $n, TokenCollection $tokens) {
139+
$len = sizeof($tokens);
140+
for($i = $n; $i < $len; $i++) {
141+
if($tokenType == $tokens->get($i)->getType()) {
142+
return $i;
143+
}
144+
}
145+
return null;
146+
}
147+
148+
public function isPrecededBy($tokenType, $n, TokenCollection $tokens, $limit = 2) {
149+
$position = $this->getPositionOfPrevious($tokenType, $n, $tokens);
150+
return ($n - $position <= $limit);
151+
}
152+
153+
public function isFollowedBy($tokenType, $n, TokenCollection $tokens, $limit = 2) {
154+
$position = $this->getPositionOfNext($tokenType, $n, $tokens);
155+
return ($position - $n >= $limit);
156+
}
128157
};

src/Hal/Component/OOP/Reflected/ReflectedMethod.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
*/
2020
class ReflectedMethod {
2121

22+
CONST VISIBILITY_PUBLIC = 1;
23+
CONST VISIBILITY_PRIVATE = 2;
24+
CONST VISIBILITY_PROTECTED = 3;
25+
CONST STATE_LOCAL = 1;
26+
CONST STATE_STATIC = 2;
2227

2328

2429
/**
@@ -75,6 +80,16 @@ class ReflectedMethod {
7580
*/
7681
private $usage;
7782

83+
/**
84+
* @var int
85+
*/
86+
private $visibility = self::VISIBILITY_PUBLIC;
87+
88+
/**
89+
* @var int
90+
*/
91+
private $state = self::STATE_LOCAL;
92+
7893
/**
7994
* Anonymous class contained in this method
8095
*
@@ -282,6 +297,41 @@ public function isGetter() {
282297
return MethodUsage::USAGE_GETTER == $this->getUsage();
283298
}
284299

300+
/**
301+
* @return int
302+
*/
303+
public function getVisibility()
304+
{
305+
return $this->visibility;
306+
}
307+
308+
/**
309+
* @param int $visibility
310+
* @return $this
311+
*/
312+
public function setVisibility($visibility)
313+
{
314+
$this->visibility = $visibility;
315+
return $this;
316+
}
317+
318+
/**
319+
* @return int
320+
*/
321+
public function getState()
322+
{
323+
return $this->state;
324+
}
325+
326+
/**
327+
* @param int $state
328+
* @return ReflectedMethod
329+
*/
330+
public function setState($state)
331+
{
332+
$this->state = $state;
333+
return $this;
334+
}
285335
/**
286336
* @param ReflectedAnonymousClass $class
287337
* @return $this

tests/Hal/Component/OOP/MethodExtractorTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace Test\Hal\Component\OOP;
33

4+
use Hal\Component\OOP\Reflected\ReflectedMethod;
45
use Hal\Component\Token\TokenCollection;
56
use Hal\Metrics\Design\Component\MaintainabilityIndex\MaintainabilityIndex;
67
use Hal\Metrics\Design\Component\MaintainabilityIndex\Result;
@@ -177,4 +178,49 @@ public function testSetterAreFound() {
177178
$this->assertTrue($methods['setA']->isSetter());
178179
$this->assertTrue($methods['setB']->isSetter());
179180
}
181+
182+
/**
183+
* @dataProvider provideCodeForVisibility
184+
*/
185+
public function testVisibilityIsFound($filename, $expected) {
186+
$extractor = new Extractor(new \Hal\Component\Token\Tokenizer());
187+
$result = $extractor->extract($filename);
188+
$classes = $result->getClasses();
189+
$class = $classes[0];
190+
$methods = $class->getMethods();
191+
$method = $methods['foo'];
192+
$this->assertEquals($expected, $method->getVisibility());
193+
}
194+
195+
public function provideCodeForVisibility() {
196+
return array(
197+
array(__DIR__.'/../../../resources/oop/visibility1.php', ReflectedMethod::VISIBILITY_PUBLIC,'undeclared visibility is public'),
198+
array(__DIR__.'/../../../resources/oop/visibility2.php', ReflectedMethod::VISIBILITY_PUBLIC, 'public is found'),
199+
array(__DIR__.'/../../../resources/oop/visibility3.php', ReflectedMethod::VISIBILITY_PRIVATE, 'private is found'),
200+
array(__DIR__.'/../../../resources/oop/visibility4.php', ReflectedMethod::VISIBILITY_PROTECTED, 'protected is found'),
201+
);
202+
}
203+
204+
/**
205+
* @dataProvider provideCodeForState
206+
*/
207+
public function testStateIsFound($filename, $expected) {
208+
$extractor = new Extractor(new \Hal\Component\Token\Tokenizer());
209+
$result = $extractor->extract($filename);
210+
$classes = $result->getClasses();
211+
$class = $classes[0];
212+
$methods = $class->getMethods();
213+
$method = $methods['foo'];
214+
$this->assertEquals($expected, $method->getState());
215+
}
216+
217+
public function provideCodeForState() {
218+
return array(
219+
array(__DIR__.'/../../../resources/oop/state1.php', ReflectedMethod::STATE_LOCAL),
220+
array(__DIR__.'/../../../resources/oop/state2.php', ReflectedMethod::STATE_STATIC),
221+
array(__DIR__.'/../../../resources/oop/state3.php', ReflectedMethod::STATE_STATIC),
222+
array(__DIR__.'/../../../resources/oop/state4.php', ReflectedMethod::STATE_STATIC),
223+
array(__DIR__.'/../../../resources/oop/state5.php', ReflectedMethod::STATE_LOCAL),
224+
);
225+
}
180226
}

tests/Hal/Component/OOP/OOPExtractorTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ public function testClassesThatDoesNotExtendOtherClassesShouldNotHaveAParentClas
8888

8989
/**
9090
* @group php7
91-
* @group wip
9291
*/
9392
public function testInterfacesAreFound() {
9493

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
namespace Test\Hal\Component\OOP;
3+
4+
use Hal\Component\OOP\Extractor\Searcher;
5+
use Hal\Component\OOP\Reflected\ReflectedInterface;
6+
use Hal\Component\Token\TokenCollection;
7+
use Hal\Component\Token\Tokenizer;
8+
use Hal\Metrics\Design\Component\MaintainabilityIndex\MaintainabilityIndex;
9+
use Hal\Metrics\Design\Component\MaintainabilityIndex\Result;
10+
use Hal\Component\OOP\Extractor\Extractor;
11+
12+
/**
13+
* @group oop
14+
*/
15+
class SearcherTest extends \PHPUnit_Framework_TestCase {
16+
17+
18+
public function testPreviousIsFound() {
19+
$code = '<?php class A { public function foo(){} }';
20+
$tokens = new TokenCollection(token_get_all($code));
21+
$end = sizeof($tokens) - 1;
22+
23+
$searcher = new Searcher();
24+
$position = $searcher->getPositionOfPrevious(T_PUBLIC, $end, $tokens);
25+
$this->assertEquals(7, $position);
26+
}
27+
28+
public function testIsPrecededWorks() {
29+
$code = '<?php class A { public function foo(){} }';
30+
$tokens = new TokenCollection(token_get_all($code));
31+
$positionOfMethod = 9;
32+
33+
$searcher = new Searcher();
34+
$this->assertTrue($searcher->isPrecededBy(T_PUBLIC, $positionOfMethod, $tokens));
35+
$this->assertFalse($searcher->isPrecededBy(T_PRIVATE, $positionOfMethod, $tokens));
36+
}
37+
38+
public function testNextIsFound() {
39+
$code = '<?php class A { public function foo(){} }';
40+
$tokens = new TokenCollection(token_get_all($code));
41+
$searcher = new Searcher();
42+
$position = $searcher->getPositionOfNext(T_PUBLIC, 1, $tokens);
43+
$this->assertEquals(7, $position);
44+
}
45+
46+
public function testIsFollowedWorks() {
47+
$code = '<?php class A { public function foo(){} }';
48+
$tokens = new TokenCollection(token_get_all($code));
49+
$searcher = new Searcher();
50+
$this->assertTrue($searcher->isFollowedBy(T_FUNCTION, 7, $tokens));
51+
}
52+
53+
}

tests/resources/oop/state1.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
class A {
3+
function foo() {}
4+
}

tests/resources/oop/state2.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
class A {
3+
static function foo() {}
4+
}

tests/resources/oop/state3.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
class A {
3+
public static function foo() {}
4+
}

tests/resources/oop/state4.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
class A {
3+
static public function foo() {}
4+
}

0 commit comments

Comments
 (0)