Completed
Push — master ( 94ad02...ce2496 )
by Richard
02:55
created

Value::parseTokens()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 2 Features 1
Metric Value
c 10
b 2
f 1
dl 0
loc 14
rs 8.8571
cc 5
eloc 9
nc 4
nop 2
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
		Stores the last value e.g.
15
			"a" + "b"
16
		Will store "a" before reading the token for the + and perfoming the concatenate operation
17
	*/
18
	private $last;
19
	private $data;
20
	private $result;
21
22
	private $tokenFuncs = [
23
			Tokenizer::NOT => 'processComparator',
24
			Tokenizer::EQUALS => 'processComparator',
25
			Tokenizer::DOT => 'processDot',
26
			Tokenizer::OPEN_SQUARE_BRACKET => 'processSquareBracket',
27
			Tokenizer::ARG => 'processSeparator',
28
			Tokenizer::CONCAT => 'processSeparator',
29
			Tokenizer::NAME => 'processScalar',
30
			Tokenizer::NUMERIC => 'processString',
31
			Tokenizer::BOOL => 'processString',
32
			Tokenizer::STRING => 'processString',
33
			Tokenizer::OPEN_BRACKET => 'processBrackets'
34
	];
35
36
	public function __construct($data, $autoLookup = false) {
37
		$this->baseData = $data;
38
		$this->autoLookup = $autoLookup;
39
	}
40
41
	public function parse($str) {
42
		$tokenizer = new Tokenizer($str);
43
		$tokens = $tokenizer->getTokens();
44
		$this->result = $this->parseTokens($tokens, $this->baseData);
45
		return $this->result;
46
	}
47
48
	public function parseTokens($tokens, $data = null) {
49
		$this->result = new ValueResult;
50
		$this->data = new ValueData($data ? $data : $this->baseData);
51
		$this->last = null;
52
53
		if (empty($tokens)) return [$data];
54
55
		foreach ($tokens as $token) {
56
			if ($token['type'] !== Tokenizer::WHITESPACE) $this->{$this->tokenFuncs[$token['type']]}($token);
57
		}
58
59
		$this->processLast();
60
		return $this->result->getResult();
61
	}
62
63
	private function processComparator($token) {
64
		$this->processLast();
65
66
		if (!(in_array($this->result->getMode(), array_keys($this->tokenFuncs, 'processComparator')) && $token['type'] == Tokenizer::EQUALS)) {
67
			$this->result->setMode($token['type']);
68
			$this->last = null;
69
		}
70
	}
71
72
	//Reads the last selected value from $data regardless if it's an array or object and overrides $this->data with the new value
73
	//Dot moves $data to the next object in $data foo.bar moves the $data pointer from `foo` to `bar`
74
	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...
75
		if ($this->last !== null) $this->data->traverse($this->last);
76
		else {
77
			//When . is not preceeded by anything, treat it as part of the string instead of an operator
78
			// foo.bar is treated as looking up `bar` in `foo` whereas .foo is treated as the string ".foo"
79
			$lastResult = $this->result->pop();
80
			if ($lastResult) $this->data = new ValueData($lastResult);
81
			else {
82
				$this->processString(['value' => '.']);
83
				$this->result->setMode(Tokenizer::CONCAT);
84
			}
85
		}
86
87
		$this->last = null;
88
	}
89
90
	private function processSquareBracket($token) {
91
		$parser = new Value($this->baseData, $this->autoLookup);
92
		if ($this->baseData instanceof \Transphporm\Functionset && $this->baseData->hasFunction($this->last)) {
93
			$this->callTransphpormFunctions($token);
94
		}
95
		else {
96
			if ($this->last !== null) $this->data->traverse($this->last);
97
			$this->last = $parser->parseTokens($token['value'], null)[0];
98
		}
99
	}
100
101
	private function processSeparator($token) {
102
		$this->result->setMode($token['type']);
103
	}
104
105
	private function processScalar($token) {
106
		$this->last = $token['value'];
107
	}
108
109
	private function processString($token) {
110
		$this->result->processValue($token['value']);
111
	}
112
113
	private function processBrackets($token) {
114
		if ($this->baseData instanceof \Transphporm\Functionset && $this->baseData->hasFunction($this->last)) {
115
			$this->callTransphpormFunctions($token);
116
		}
117
		else {
118
			$this->processNested($token);
119
		}
120
	}
121
122
	private function processNested($token) {
123
		$parser = new Value($this->baseData, $this->autoLookup);
124
		$funcResult = $this->data->parseNested($parser, $token, $this->last);
125
		$this->result->processValue($funcResult);
126
		$this->last = null;
127
	}
128
129
	private function callTransphpormFunctions($token) {
130
		$this->result->processValue($this->baseData->{$this->last}($token['value']));
131
		foreach ($this->result->getResult() as $i => $value) {
132
			if (is_scalar($value)) {
133
				$val = $this->data->read($value);
134
				if ($val) $this->result[$i] = $val;
135
			}
136
		}
137
		$this->last = null;
138
	}
139
140
	//Applies the current operation to whatever is in $last based on $mode
141
	private function processLast() {
142
		if ($this->last !== null) {
143
			try {
144
				$value = $this->data->extract($this->last, $this->autoLookup);
145
				$this->result->processValue($value);
146
			}
147
			catch (\UnexpectedValueException $e) {
148
				if (!$this->autoLookup) {
149
					$this->result->processValue($this->last);
150
				}
151
				else {
152
					$this->result->clear();
153
					$this->result[0] = false;
154
				}
155
			}
156
		}
157
	}
158
}
159