Value   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 5
dl 0
loc 154
rs 9.84
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A parse() 0 6 1
A parseTokens() 0 18 6
A processComparator() 0 9 3
A processDot() 0 15 3
A hasFunction() 0 3 2
A processSquareBracket() 0 10 3
A processSeparator() 0 3 1
A processScalar() 0 6 2
A processString() 0 3 1
A processBrackets() 0 9 3
A getNewParser() 0 3 1
A callTransphpormFunctions() 0 15 5
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
		if (count($tokens) === 0) return [$data];
58
59
		$this->result = new ValueResult();
60
		$this->data = new ValueData($data ? $data : $this->baseData);
61
		$this->last = new Last($this->data, $this->result, $this->autoLookup);
62
		$this->traversing = false;
63
64
65
66
		foreach ($tokens as $name => $token) {
67
			if ($token['type'] == 'WHITESPACE' || $token['type'] == 'NEWLINE') continue;
68
			$this->{$this->tokenFuncs[$token['type']]}($token);
69
		}
70
71
		$this->last->process();
72
		return $this->result->getResult();
73
	}
74
75
	private function processComparator($token) {
76
		$this->allowNullResult = false;
77
		$this->last->process();
78
79
		if (!(in_array($this->result->getMode(), array_keys($this->tokenFuncs, 'processComparator')) && $token['type'] == Tokenizer::EQUALS)) {
80
			$this->result->setMode($token['type']);
81
			$this->last->clear();
82
		}
83
	}
84
85
	//Reads the last selected value from $data regardless if it's an array or object and overrides $this->data with the new value
86
	//Dot moves $data to the next object in $data foo.bar moves the $data pointer from `foo` to `bar`
87
	private function processDot() {
88
		$lastResult = $this->last->traverse();
89
90
		//When . is not preceeded by anything, treat it as part of the string instead of an operator
91
		// foo.bar is treated as looking up `bar` in `foo` whereas .foo is treated as the string ".foo"
92
		if ($lastResult) {
93
			$this->last->makeTraversing();
94
		}
95
		else if ($this->last->isEmpty())  {
96
			$this->processString(['value' => '.']);
97
			$this->result->setMode(Tokenizer::CONCAT);
98
		}
99
100
		$this->last->clear();
101
	}
102
103
	private function hasFunction($name) {
104
		return $this->baseData instanceof \Transphporm\Functionset && $this->baseData->hasFunction($name);
105
	}
106
107
	private function processSquareBracket($token) {
108
		if ($this->hasFunction($this->last->read())) {
109
			$this->callTransphpormFunctions($token);
110
		}
111
		else {
112
			$this->last->traverse();
113
			$this->last->set($this->getNewParser()->parseTokens($token['value'], null)[0]);
114
			if (!is_bool($this->last->read())) $this->last->makeTraversing();
115
		}
116
	}
117
118
	private function processSeparator($token) {
119
		$this->result->setMode($token['type']);
120
	}
121
122
	private function processScalar($token) {
123
		if (is_scalar($this->last->read())) {
124
			$this->result->processValue($this->last->read());
125
		}
126
		$this->last->set($token['value']);
127
	}
128
129
	private function processString($token) {
130
		$this->result->processValue($token['value']);
131
	}
132
133
	private function processBrackets($token) {
134
		if ($this->hasFunction($this->last->read())
135
			&& !$this->data->methodExists($this->last->read())) {
136
			$this->callTransphpormFunctions($token);
137
		}
138
		else {
139
			$this->last->processNested($this->getNewParser(), $token);
140
		}
141
	}
142
143
	private function getNewParser() {
144
		return new Value($this->baseData, $this->autoLookup);
145
	}
146
147
	private function callTransphpormFunctions($token) {
148
		$val = $this->baseData->{$this->last->read()}($token['value']);
149
		$this->result->processValue($val);
150
151
		if ($this->autoLookup && is_string($val) && $this->allowNullResult) {
152
			$parser = new Value($this->data->getData());
153
			$parsedArr = $parser->parse($val);
154
			$parsedVal = isset($parsedArr[0]) ? $parsedArr[0] : null;
155
		}
156
		else $parsedVal = null;
157
158
        $this->result->postProcess($this->data, $val, $parsedVal, $this->allowNullResult);
159
160
		$this->last->clear();
161
	}
162
}
163