Completed
Push — master ( 4ed61c...926afa )
by Tom
03:17
created

Value::processScalar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2015 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.0                                                             */
7
namespace Transphporm\Parser;
8
/** Parses "string" and function(args) e.g. data(foo) or iteration(bar) */
9
class Value {
10
	private $baseData;
11
	private $autoLookup;
12
	private $tokens;
0 ignored issues
show
Unused Code introduced by
The property $tokens is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
13
14
	/*
15
		The next operation to perform. Will be one of the following:
16
			ARG - A new value e.g,  "a","b"  becomes ["a", "b"]
17
			CONCAT - Concat onto the current arg e.g "a" + "b" becomes ["ab"]
18
			NOT - Boolean operation "a" != "b" becomes [true]
19
			EQUALS - Boolean operation "a" = "b" becomes [false]
20
	*/
21
	private $mode;
22
23
	/*
24
		Stores the last value e.g. 
25
			"a" + "b"
26
		Will store "a" before reading the token for the + and perfoming the concatenate operation
27
	*/
28
	private $last;
29
	private $data;
30
	private $result;
31
	private $element;
32
33
	private $tokenFuncs = [
34
			Tokenizer::NOT => 'processComparator',
35
			Tokenizer::EQUALS => 'processComparator',
36
			Tokenizer::DOT => 'processDot',
37
			Tokenizer::OPEN_SQUARE_BRACKET => 'processSquareBracket',
38
			Tokenizer::ARG => 'processSeparator',
39
			Tokenizer::CONCAT => 'processSeparator',
40
			Tokenizer::NAME => 'processScalar',
41
			Tokenizer::NUMERIC => 'processScalar',
42
			Tokenizer::BOOL => 'processScalar',
43
			Tokenizer::STRING => 'processString',
44
			Tokenizer::OPEN_BRACKET => 'processBrackets'
45
	];
46
47
	public function __construct($data, $autoLookup = false) {
48
		$this->baseData = $data;
49
		$this->autoLookup = $autoLookup;
50
	}
51
52
	public function parse($str, $element = null, $returnTokens = false) {
53
		$tokenizer = new Tokenizer($str);
54
		$tokens = $tokenizer->getTokens();
55
		if ($returnTokens) return $tokens;
56
		$this->result = $this->parseTokens($tokens, $element, $this->baseData);
57
		return $this->result;
58
	}
59
60
	public function parseTokens($tokens, $element, $data) {
61
		$this->result = [];
62
		$this->mode = Tokenizer::ARG;
63
		$this->data = $data;
64
		$this->last = null;
65
		$this->element = $element;
66
67
		if (empty($tokens)) return [$this->data];
68
		
69
		foreach ($tokens as $token) {
70
			$this->{$this->tokenFuncs[$token['type']]}($token);	
71
		}
72
73
		return $this->processLast();
74
	}
75
76
	private function processComparator($token) {
77
		$this->result = $this->processLast();
78
79
		if ($this->mode == Tokenizer::NOT && $token['type'] == Tokenizer::EQUALS) {
80
			$this->mode = Tokenizer::NOT;
81
		}
82
		else $this->mode = $token['type'];
83
	}
84
85
86
	//Reads the last selected value from $data regardless if it's an array or object and overrides $this->data with the new value
87
	private function moveLastToData() {
88
		if (isset($this->data->{$this->last})) $this->data = $this->data->{$this->last};
89 View Code Duplication
		else if (is_array($this->data) && isset($this->data[$this->last])) $this->data = $this->data[$this->last];
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90
	}
91
92
	//Dot moves $data to the next object in $data foo.bar moves the $data pointer from `foo` to `bar`
93
	private function processDot($token) {
0 ignored issues
show
Unused Code introduced by
The parameter $token is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
		if ($this->last !== null) $this->moveLastToData();
95
		else $this->data = array_pop($this->result);
96
97
		$this->last = null;
98
	}
99
100
	private function processSquareBracket($token) {
101
		if ($this->last !== null) $this->moveLastToData();
102
103
		$parser = new Value($this->baseData, $this->autoLookup);
104
		$this->last = $parser->parseTokens($token['value'], $this->element, null)[0];
105
	}
106
107
	private function processSeparator($token) {
108
		$this->mode = $token['type'];
109
		//if ($this->last !== null) $this->result = $this->processValue($this->result, $this->mode, $this->last);
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
110
		$this->result = $this->processLast();
111
	}
112
113
	private function processScalar($token) {
114
		$this->last = $token['value'];
115
	}
116
117
	private function processString($token) {
118
		$this->result = $this->processValue($token['value']);
119
	}
120
121
	private function processBrackets($token) {
122
		if ($this->baseData instanceof \Transphporm\Functionset && $this->baseData->hasFunction($this->last)) {
123
			$this->callTransphpormFunctions($token);
124
		}
125
		else if ($this->data instanceof \Transphporm\Functionset) {
126
			$this->result = $this->processValue($this->data->{$this->last}($token['value'], $this->element));
127
			$this->last = null;
128
		}
129
		else {
130
			$parser = new Value($this->baseData, $this->autoLookup);
131
			$args = $parser->parseTokens($token['value'], $this->element, $this->data);
132
			if ($args[0] == $this->data) $args = [];
133
			$funcResult = $this->callFunc($this->last, $args, $this->element, $this->data);
134
			$this->result = $this->processValue($funcResult);
135
			$this->last = null;
136
		}
137
	}
138
139
	private function callTransphpormFunctions($token) {
140
		$this->result = $this->processValue($this->baseData->{$this->last}($token['value'], $this->element));
141
		foreach ($this->result as $i => $value) {
142
			if (is_array($this->data)) {
143
				if (isset($this->data[$value])) $this->result[$i] = $this->data[$value];
144
			}
145
			else if (is_scalar($value) && isset($this->data->$value)) $this->result[$i] = $this->data->$value;
146
		}
147
		$this->last = null;
148
	}
149
150
	//Applies the current operation to whatever is in $last based on $mode
151
	private function processLast() {
152
		if ($this->last !== null) {
153
			try {
154
				$this->result = $this->extractLast($this->result);
155
			}
156
			catch (\UnexpectedValueException $e) {
157
				if (!$this->autoLookup) {
158
					$this->result = $this->processValue($this->last);
159
				}
160
				else $this->result = [false];			
161
			}			
162
		}
163
		return $this->result;
164
	}
165
166
167
	//Extracts $last from $data. If "last" is "bar" from value "foo.bar",
168
	//$data contains "foo" and this function reads $data[$bar] or $data->$bar
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
169
	private function extractLast($result) {
0 ignored issues
show
Unused Code introduced by
The parameter $result is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
		if ($this->autoLookup && isset($this->data->{$this->last})) {
171
			return $this->processValue($this->data->{$this->last});
172
		}
173 View Code Duplication
		else if (is_array($this->data) && isset($this->data[$this->last])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
174
			return $this->processValue($this->data[$this->last]);
175
		}
176
		throw new \UnexpectedValueException('Not found');
177
	}	
178
179
	//Processes $newValue using $mode. Either concats to the current argument, adds a new argument
180
	//Or usess the two arguments for a boolean comparison
181
	private function processValue($newValue) {
182
		if ($this->mode == Tokenizer::ARG) {
183
			$this->result[] = $newValue;
184
		}
185
		else if ($this->mode == Tokenizer::CONCAT) {
186
				$this->result[count($this->result)-1] .= $newValue;
187
		}
188
		else if ($this->mode == Tokenizer::NOT) {
189
			$this->result[count($this->result)-1] = $this->result[count($this->result)-1] != $newValue;
190
		}
191
		else if ($this->mode == Tokenizer::EQUALS) {
192
			$this->result[count($this->result)-1] = $this->result[count($this->result)-1] == $newValue;
193
		}
194
195
		return $this->result;
196
	}
197
198
	private function callFunc($name, $args, $element, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
199
		if ($this->data instanceof \Transphporm\FunctionSet) return $this->data->$name($args, $element);
200
		else return $this->callFuncOnObject($this->data, $name, $args, $element);
201
	}
202
203
	private function callFuncOnObject($obj, $func, $args, $element) {
0 ignored issues
show
Unused Code introduced by
The parameter $element is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
204
		if (isset($obj->$func) && is_callable($obj->$func)) return call_user_func_array($obj->$func, $args);
205
		else return call_user_func_array([$obj, $func], $args);
206
	}
207
}
208