Completed
Pull Request — master (#64)
by Tom
02:17
created

Value::appendToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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