Completed
Push — master ( 9fb92e...1099f2 )
by Tom
02:18
created

Value::parseString()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 9.2
cc 4
eloc 12
nc 3
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 $dataFunction;
11
	private $callParamsAsArray;
12
	private $parent;
13
14
	public function __construct($dataFunction, Value $parent = null, $callParamsAsArray = true) {
15
		$this->dataFunction = $dataFunction;
16
		$this->callParamsAsArray = $callParamsAsArray;
17
		$this->parent = $parent;
18
	}
19
20
	private function extractQuotedString($marker, $str) {
21
		$finalPos = $this->findMatchingPos($str, $marker);
22
		$string = substr($str, 1, $finalPos-1);
23
		//Now remove escape characters
24
		return str_replace('\\' . $marker, $marker, $string);
25
	}
26
27
	private function parseFunction($function) {
28
		$open = strpos($function, '(');
29
		if ($open) {
30
			$name = substr($function, 0, $open);
31
			$bracketMatcher = new BracketMatcher($function);
32
			$params = $bracketMatcher->match('(', ')');
33
			
34
			return ['name' => $name, 'params' => $params, 'endPoint' => $bracketMatcher->getClosePos()];
35
		}
36
		else return ['name' => null, 'params' => $function, 'endPoint' => strlen($function)];
37
	}
38
39
	public function parse($function, \DomElement $element) {
40
		$stringExtractor = new StringExtractor($function);
41
		$parts = explode('+', $stringExtractor);
42
43
		$result = [];
44
		foreach ($parts as $part) {
45
			$part = $stringExtractor->rebuild($part);
46
			$result = array_merge($result, $this->parseString(trim($part), $element));
47
		}
48
49
		return $result;	
50
	}
51
52
	private function parseString($function, $element) {
53
		$result = [];
54
		if ($function && in_array($function[0], ['\'', '"'])) {
55
			$finalPos = $this->findMatchingPos($function, $function[0]);
56
			$result[] = $this->extractQuotedString($function[0], $function);
57
		}
58
		else {
59
			$func = $this->parseFunction($function);
60
			$finalPos = $func['endPoint'];			
61
62
			if (($data = $this->getFunctionValue($func['name'], $func['params'], $element)) !== false) $result = $this->appendToArray($result, $data);
63
			else $result[] = trim($function);
64
		}
65
		$remaining = trim(substr($function, $finalPos+1));
66
		return $this->parseNextValue($remaining, $result, $element);
67
	}
68
69
	private function getFunctionValue($name, $params, $element) {
70
		if (($data = $this->callFunc($name, $params, $element)) !== false) {
71
			return $data;
72
		}
73
		else if ($this->parent != null && ($data = $this->parent->callFunc($name, $params, $element)) !== false) {
74
			return $data;
75
		}
76
		else return false;
77
	}
78
79
	private function appendToArray($array, $value) {
80
		if (is_array($value)) $array += $value;
81
		else $array[] = $value;
82
		return $array;
83
	}
84
85
	private function callFunc($name, $params, $element) {
86
		if ($name && $this->isCallable($this->dataFunction, $name)) {
87
			if ($this->callParamsAsArray) return $this->dataFunction->$name($this->parse($params, $element), $element);	
88
			else {
89
				return $this->callFuncOnObject($this->dataFunction, $name, $this->parse($params, $element));
90
			}
91
		}
92
		return false;
93
	}
94
95
	//is_callable does not detect closures on properties, only methods defined in the class!
96
	private function isCallable($obj, $func) {
97
		return (isset($obj->$func) && is_callable($obj->$func)) || is_callable([$obj, $func]);
98
	}
99
100
	private function callFuncOnObject($obj, $func, $params) {
101
		if (isset($obj->$func) && is_callable($obj->$func)) return call_user_func_array($obj->$func, $params);
102
		else return call_user_func_array([$obj, $func], $params);
103
	}
104
105
	private function parseNextValue($remaining, $result, $element) {
106
		if (strlen($remaining) > 0 && $remaining[0] == ',') $result = array_merge($result, $this->parse(trim(substr($remaining, 1)), $element));
107
		return $result;
108
	}
109
	
110
	private function findMatchingPos($string, $char, $start = 0, $escape = '\\') {
111
		$pos = $start+1;
112
		$end = 0;
0 ignored issues
show
Unused Code introduced by
$end is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
113
		while ($end = strpos($string, $char, $pos)) {
114
			if ($string[$end-1] === $escape) $pos = $end+1;
115
			else {
116
				break;
117
			}
118
		}
119
		return $end;
120
	}
121
}