Completed
Push — master ( 35ed97...34813d )
by Tom
03:50 queued 01:34
created

Value::processIn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2017 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.2                                                             */
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
	/*
13
		Stores the last value e.g.
14
			"a" + "b"
15
		Will store "a" before reading the token for the + and perfoming the concatenate operation
16
	*/
17
	private $last;
18
	private $data;
19
	private $result;
20
	private $traversing = false;
21
	private $allowNullResult = false;
22
23
	private $tokenFuncs = [
24
			Tokenizer::NOT => 'processComparator',
25
			Tokenizer::EQUALS => 'processComparator',
26
			Tokenizer::DOT => 'processDot',
27
			Tokenizer::OPEN_SQUARE_BRACKET => 'processSquareBracket',
28
			Tokenizer::ARG => 'processSeparator',
29
			Tokenizer::CONCAT => 'processSeparator',
30
			Tokenizer::SUBTRACT => 'processSeparator',
31
			Tokenizer::MULTIPLY => 'processSeparator',
32
			Tokenizer::DIVIDE => 'processSeparator',
33
			Tokenizer::NAME => 'processScalar',
34
			Tokenizer::NUMERIC => 'processString',
35
			Tokenizer::BOOL => 'processString',
36
			Tokenizer::STRING => 'processString',
37
			Tokenizer::OPEN_BRACKET => 'processBrackets',
38
			Tokenizer::GREATER_THAN => 'processComparator',
39
			Tokenizer::LOWER_THAN => 'processComparator',
40
			Tokenizer::IN => 'processComparator'
41
		];
42
43
	public function __construct($data, $autoLookup = false, $allowNullResult = false) {
44
		$this->baseData = $data;
45
		$this->autoLookup = $autoLookup;
46
		$this->allowNullResult = $allowNullResult;
47
	}
48
49
	public function parse($str) {
50
		$tokenizer = new Tokenizer($str);
51
		$tokens = $tokenizer->getTokens();
52
		$this->result = $this->parseTokens($tokens, $this->baseData);
53
		return $this->result;
54
	}
55
56
	public function parseTokens($tokens, $data = null) {
57
		$this->result = new ValueResult();
58
		$this->data = new ValueData($data ? $data : $this->baseData);
59
		$this->last = new Last($this->data, $this->result, $this->autoLookup);
60
		$this->traversing = false;
61
62
		if (count($tokens) <= 0) return [$data];
63
64
		foreach (new TokenFilterIterator($tokens, [Tokenizer::WHITESPACE, Tokenizer::NEW_LINE]) as $name => $token) {
65
			$this->{$this->tokenFuncs[$token['type']]}($token);
66
		}
67
68
		$this->last->process();
69
		return $this->result->getResult();
70
	}
71
72
	private function processComparator($token) {
73
		$this->allowNullResult = false;
74
		$this->last->process();
75
76
		if (!(in_array($this->result->getMode(), array_keys($this->tokenFuncs, 'processComparator')) && $token['type'] == Tokenizer::EQUALS)) {
77
			$this->result->setMode($token['type']);
78
			$this->last->clear();
79
		}
80
	}
81
82
	//Reads the last selected value from $data regardless if it's an array or object and overrides $this->data with the new value
83
	//Dot moves $data to the next object in $data foo.bar moves the $data pointer from `foo` to `bar`
84
	private function processDot() {
85
		$lastResult = $this->last->traverse();
86
87
		//When . is not preceeded by anything, treat it as part of the string instead of an operator
88
		// foo.bar is treated as looking up `bar` in `foo` whereas .foo is treated as the string ".foo"
89
		if ($lastResult) {
90
			$this->last->makeTraversing();
91
		}
92
		else if ($this->last->isEmpty())  {
93
			$this->processString(['value' => '.']);
94
			$this->result->setMode(Tokenizer::CONCAT);
95
		}
96
97
		$this->last->clear();
98
	}
99
100
	private function hasFunction($name) {
101
		return $this->baseData instanceof \Transphporm\Functionset && $this->baseData->hasFunction($name);
102
	}
103
104
	private function processSquareBracket($token) {
105
		if ($this->hasFunction($this->last->read())) {
106
			$this->callTransphpormFunctions($token);
107
		}
108
		else {
109
			$this->last->traverse();
110
			$this->last->set($this->getNewParser()->parseTokens($token['value'], null)[0]);
111
			if (!is_bool($this->last->read())) $this->last->makeTraversing();
112
		}
113
	}
114
115
	private function processSeparator($token) {
116
		$this->result->setMode($token['type']);
117
	}
118
119
	private function processScalar($token) {
120
		if (is_scalar($this->last->read())) {
121
			$this->result->processValue($this->last->read());
122
		}
123
		$this->last->set($token['value']);
124
	}
125
126
	private function processString($token) {
127
		$this->result->processValue($token['value']);
128
	}
129
130
	private function processBrackets($token) {
131
		if ($this->hasFunction($this->last->read())
132
			&& !$this->data->methodExists($this->last->read())) {
133
			$this->callTransphpormFunctions($token);
134
		}
135
		else {
136
			$this->last->processNested($this->getNewParser(), $token);
137
		}
138
	}
139
140
	private function getNewParser() {
141
		return new Value($this->baseData, $this->autoLookup);
142
	}
143
144
	private function callTransphpormFunctions($token) {
145
		$val = $this->baseData->{$this->last->read()}($token['value']);
146
		$this->result->processValue($val);
147
148
		if ($this->autoLookup) {
149
			if (!is_array($val)) {
150
				$parser = new Value($this->data->getData());
151
				$parsedArr = $parser->parse($val);
152
				$parsedVal = isset($parsedArr[0]) ? $parsedArr[0] : null;
153
			}
154
			else $parsedVal = $val;
155
		}
156
		else $parsedVal = null;
157
158
        $this->result->postProcess($this->data, $val, $parsedVal, $this->allowNullResult);
159
160
		$this->last->clear();
161
	}
162
}
163