MethodCallsCollector   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 47
c 1
b 0
f 0
dl 0
loc 109
rs 10
wmc 18

2 Methods

Rating   Name   Duplication   Size   Complexity  
B collect() 0 73 11
B resolve() 0 32 7
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: peter
5
 * Date: 20.07.18
6
 * Time: 14:21
7
 */
8
9
namespace Maslosoft\Whitelist\Tokenizer\Collectors;
10
11
12
use function array_unshift;
13
use function implode;
14
use Maslosoft\Whitelist\Interfaces\TokenCollectorInterface;
15
use Maslosoft\Whitelist\Interfaces\TokenInterface;
16
use Maslosoft\Whitelist\Tokenizer\Composite\StaticMethod;
17
use const T_DOUBLE_COLON;
18
use const T_NEW;
19
use const T_OBJECT_OPERATOR;
20
use const T_STRING;
21
use const T_VARIABLE;
22
23
class MethodCallsCollector implements TokenCollectorInterface
24
{
25
	public function collect($tokens)
26
	{
27
		$result = [];
28
		foreach ($tokens as $index => $token)
29
		{
30
			// Skip all except object operator
31
			if($token->not(T_OBJECT_OPERATOR))
32
			{
33
				continue;
34
			}
35
			$prev = $token->prev();
36
			$next = $token->next();
37
38
			// Should start with variable OR closing parenthesis
39
			// eg:
40
			// $object->call()
41
			// (new Object)->call()
42
			// $objects[1]->call()
43
			if($prev->not(T_VARIABLE) xor $prev->value !== ')' xor $prev->value !== ']')
44
			{
45
				continue;
46
			}
47
48
			// Should have method name
49
			if($next->not(T_STRING))
50
			{
51
				continue;
52
			}
53
54
			$finishing = $next->next();
55
56
			// Should have opening bracket after method name
57
			if($finishing->value !== '(')
58
			{
59
				continue;
60
			}
61
62
			// TODO Resolve use statements
63
			$className = [];
64
			$className[] = $prev->value;
65
66
			// Case of (new Type)->method() call
67
			$this->resolve($className, $prev, '(', ')');
68
69
			// Case of [Something]->call()
70
			$this->resolve($className, $prev, '[', ']');
71
72
			// Method call result, eg:
73
			// myFunc()->method()
74
			// This does not (yet?) support nested calls like:
75
			// EG.: Yii::app()->getClientScript()->registerScript()
76
			if(implode('', $className) === '()')
77
			{
78
				$function = $prev->prev()->prev();
79
				array_unshift($className, $function->value);
80
				$calledFrom = $function->prev();
81
				if($calledFrom->is(T_DOUBLE_COLON) || $calledFrom->is(T_OBJECT_OPERATOR))
82
				{
83
					array_unshift($className, $calledFrom->value);
84
					$parent = $calledFrom->prev();
85
					if($parent->is(T_STRING) || $parent->is(T_VARIABLE))
86
					{
87
						array_unshift($className,$parent->value);
88
					}
89
				}
90
			}
91
92
			$methodName = $next->value;
93
			$name = sprintf('%s->%s', implode($className), $methodName);
94
95
			$result[] = new StaticMethod($name,$tokens, $index);
96
		}
97
		return $result;
98
	}
99
100
	private function resolve(&$className, TokenInterface $prev, $opened, $until)
101
	{
102
		$opening = null;
103
		if($prev->value === $until)
104
		{
105
			$part = $prev->value;
106
			$opening = $prev;
107
			while($part !== $opened)
108
			{
109
				$opening = $opening->prev();
110
				if(empty($opening))
111
				{
112
					break;
113
				}
114
				$part = $opening->value;
115
116
				// Ensure space after `new`
117
				if($opening->is(T_NEW))
118
				{
119
					$part = "$part ";
120
				}
121
				array_unshift($className, $part);
122
			}
123
		}
124
		if(empty($opening))
125
		{
126
			return;
127
		}
128
		$preOpening = $opening->prev();
129
		if($preOpening->is(T_VARIABLE))
130
		{
131
			array_unshift($className, $preOpening->value);
132
		}
133
	}
134
}