EnforceFQN::processConstant()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 10
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 10
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 2
nop 0
crap 3
1
<?php
2
3
/**
4
* @package   s9e\SourceOptimizer
5
* @copyright Copyright (c) 2014-2018 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\SourceOptimizer\Passes;
9
10
use s9e\SourceOptimizer\ContextHelper;
11
12
class EnforceFQN extends AbstractPass
13
{
14
	/**
15
	* @var array List of global constants (constant names used as keys)
16
	*/
17
	protected $constants;
18
19
	/**
20
	* @var array List of internal functions (function names used as keys)
21
	*/
22
	protected $functions;
23
24
	/**
25
	* Constructor
26
	*/
27 15
	public function __construct()
28
	{
29 15
		$this->constants = get_defined_constants();
30 15
		$this->functions = array_flip(get_defined_functions()['internal']);
31 15
	}
32
33
	/**
34
	* {@inheritdoc}
35
	*/
36 15
	protected function optimizeStream()
37
	{
38 15
		ContextHelper::forEachNamespace(
39 15
			$this->stream,
40 15
			function ($namespace, $startOffset, $endOffset)
41
			{
42 15
				if ($namespace !== '')
43
				{
44 15
					$this->optimizeBlock($startOffset, $endOffset);
45
				}
46 15
			}
47
		);
48 15
	}
49
50
	/**
51
	* Test whether current token is followed by an object operator or a namespace separator
52
	*
53
	* @return bool
54
	*/
55 15
	protected function isFollowedByOperator()
56
	{
57 15
		$offset = $this->stream->key();
58 15
		$this->stream->next();
59 15
		$this->stream->skipNoise();
60 15
		$isFollowedByOperator = ($this->stream->valid() && $this->stream->isAny([T_NS_SEPARATOR, T_PAAMAYIM_NEKUDOTAYIM]));
61 15
		$this->stream->seek($offset);
62
63 15
		return $isFollowedByOperator;
64
	}
65
66
	/**
67
	* Test whether current token is followed by a parenthesis
68
	*
69
	* @return bool
70
	*/
71 15
	protected function isFollowedByParenthesis()
72
	{
73 15
		$offset = $this->stream->key();
74 15
		$this->stream->next();
75 15
		$this->stream->skipNoise();
76 15
		$isFollowedByParenthesis = ($this->stream->valid() && $this->stream->current() === '(');
77 15
		$this->stream->seek($offset);
78
79 15
		return $isFollowedByParenthesis;
80
	}
81
82
	/**
83
	* Test whether the token at given offset is preceded by a keyword
84
	*
85
	* @return bool
86
	*/
87 15
	protected function isPrecededByKeyword()
88
	{
89 15
		$offset = $this->stream->key();
90 15
		while (--$offset > 0)
91
		{
92 15
			$tokenValue = $this->stream[$offset][0];
93 15
			if (in_array($tokenValue, [T_CLASS, T_CONST, T_FUNCTION, T_INTERFACE, T_NEW, T_NS_SEPARATOR, T_OBJECT_OPERATOR, T_PAAMAYIM_NEKUDOTAYIM, T_TRAIT, T_USE], true))
94
			{
95 9
				return true;
96
			}
97
98
			// Stop looking once we found a token that's not whitespace or comment
99 15
			if (!in_array($tokenValue, [T_COMMENT, T_DOC_COMMENT, T_WHITESPACE], true))
100
			{
101 15
				break;
102
			}
103
		}
104
105 15
		return false;
106
	}
107
108
	/**
109
	* Optimize all constants and function calls in given range
110
	*
111
	* @param  integer $startOffset
112
	* @param  integer $endOffset
113
	* @return void
114
	*/
115 15
	protected function optimizeBlock($startOffset, $endOffset)
116
	{
117 15
		$this->stream->seek($startOffset);
118 15
		while ($this->stream->skipTo(T_STRING) && $this->stream->key() <= $endOffset)
119
		{
120 15
			if ($this->isPrecededByKeyword() || $this->isFollowedByOperator())
121
			{
122 9
				continue;
123
			}
124 15
			if ($this->isFollowedByParenthesis())
125
			{
126 8
				$this->processFunctionCall();
127
			}
128
			else
129
			{
130 15
				$this->processConstant();
131
			}
132
		}
133 15
	}
134
135
	/**
136
	* Process the constant at current offset
137
	*
138
	* @return void
139
	*/
140 15 View Code Duplication
	protected function processConstant()
141
	{
142 15
		$constName = $this->stream->currentText();
143 15
		if (!isset($this->constants[$constName]) && !preg_match('(^(?:false|null|true)$)Di', $constName))
144
		{
145 15
			return;
146
		}
147
148 4
		$this->stream->replace([T_STRING, '\\' . $constName]);
149 4
	}
150
151
	/**
152
	* Process the function name at current offset
153
	*
154
	* @return void
155
	*/
156 8 View Code Duplication
	protected function processFunctionCall()
157
	{
158 8
		$funcName = $this->stream->currentText();
159 8
		if (!isset($this->functions[$funcName]))
160
		{
161 2
			return;
162
		}
163
164 6
		$this->stream->replace([T_STRING, '\\' . $funcName]);
165
	}
166
}