Completed
Push — master ( 9414d0...d03e02 )
by Richard
02:32
created

CssToXpath   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 13
Bugs 2 Features 1
Metric Value
wmc 27
c 13
b 2
f 1
lcom 2
cbo 0
dl 0
loc 135
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A registerInstance() 0 5 1
A createSelector() 0 6 1
A __construct() 0 15 1
B processAttr() 0 28 3
A splitOnToken() 0 9 3
B split() 0 25 6
B getXpath() 0 19 10
A getDepth() 0 3 1
A getPseudo() 0 5 1
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
class CssToXpath {
9
	private $specialChars = [Tokenizer::WHITESPACE, Tokenizer::DOT, Tokenizer::GREATER_THAN, '~', Tokenizer::NUM_SIGN, Tokenizer::COLON, Tokenizer::OPEN_SQUARE_BRACKET];
10
	private $translators = [];
11
	private $css;
0 ignored issues
show
Unused Code introduced by
The property $css 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...
12
	private $valueParser;
13
	private static $instances = [];
14
	private $functionSet;
15
16
17
	public function __construct(Value $valueParser, \Transphporm\FunctionSet $functionSet, $prefix = '') {
18
		$hash = $this->registerInstance();
19
		$this->valueParser = $valueParser;
20
		$this->functionSet = $functionSet;
21
22
		$this->translators = [
23
			Tokenizer::WHITESPACE => function($string) use ($prefix) { return '//' . $prefix . $string;	},
24
			'' => function($string) use ($prefix) { return '/' . $prefix . $string;	},
25
			Tokenizer::GREATER_THAN => function($string) use ($prefix) { return '/' . $prefix  . $string; },
26
			Tokenizer::NUM_SIGN => function($string) { return '[@id=\'' . $string . '\']'; },
27
			Tokenizer::DOT => function($string) { return '[contains(concat(\' \', normalize-space(@class), \' \'), \' ' . $string . ' \')]'; },
28
			Tokenizer::OPEN_SQUARE_BRACKET => function($string) use ($hash) { return '[' .'php:function(\'\Transphporm\Parser\CssToXpath::processAttr\', \'' . json_encode($string) . '\', ., "' . $hash . '")' . ']';	},
29
			//']' => function() {	return ''; }
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
30
		];
31
	}
32
33
	private function registerInstance() {
34
		$hash = spl_object_hash($this);
35
		self::$instances[$hash] = $this;
36
		return $hash;
37
	}
38
39
	private function createSelector() {
40
		$selector = new \stdclass;
41
		$selector->type = '';
42
		$selector->string = '';
43
		return $selector;
44
	}
45
46
	//XPath only allows registering of static functions... this is a hacky workaround for that
47
	public static function processAttr($attr, $element, $hash) {
48
		$attr = json_decode($attr, true);
49
		$valueParser = self::$instances[$hash]->valueParser;
50
		self::$instances[$hash]->functionSet->setElement($element[0]);
51
52
		$parts = self::$instances[$hash]->splitOnToken($attr, Tokenizer::EQUALS);
53
54
		if ($parts[0] === $attr) return $element[0]->getAttribute($valueParser->parseTokens($attr)[0]) !== '';
55
56
		if ($parts[0][count($parts[0])-1]['type'] === Tokenizer::NOT) {
57
			$attr = [
58
				['type' => Tokenizer::NAME, 'value' => 'attr'],
59
				['type' => Tokenizer::OPEN_BRACKET, 'value' => $parts[0]],
60
				['type' => Tokenizer::NOT],
61
				['type' => Tokenizer::EQUALS]
62
			];
63
			$attr = array_merge($attr, $parts[1]);
64
		}
65
		else {
66
			$attr = [
67
				['type' => Tokenizer::NAME, 'value' => 'attr'],
68
				['type' => Tokenizer::OPEN_BRACKET, 'value' => $parts[0]],
69
				['type' => Tokenizer::EQUALS]
70
			];
71
			$attr = array_merge($attr, $parts[1]);
72
		}
73
		return $valueParser->parseTokens($attr)[0];
74
	}
75
76
	private function splitOnToken($tokens, $splitOn) {
77
		$splitTokens = [];
78
		$i = 0;
79
		foreach ($tokens as $token) {
80
			if ($token['type'] === $splitOn) $i++;
81
			else $splitTokens[$i][] = $token;
82
		}
83
		return $splitTokens;
84
	}
85
86
	//split the css into indivudal functions
87
	private function split($css) {
88
		$selectors = [];
89
		$selector = $this->createSelector();
90
		$selectors[] = $selector;
91
92
		foreach ($css as $token) {
93
			if (in_array($token['type'], $this->specialChars)) {
94
				$selector = $this->createSelector();
95
				$selector->type = $token['type'];
96
				$selectors[] = $selector;
97
			}
98
			if (isset($token['value'])) $selectors[count($selectors)-1]->string = $token['value'];
99
		}
100
		return $selectors;
101
102
		for ($i = 0; $i < strlen($css); $i++) {
0 ignored issues
show
Unused Code introduced by
for ($i = 0; $i < strlen...ng .= $css[$i]; } } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
103
			if (in_array($css[$i], $this->specialChars)) {
104
				$selector = $this->createSelector();
105
				$selector->type = $css[$i];
106
				$selectors[] = $selector;
107
			}
108
			else $selector->string .= $css[$i];
109
		}
110
		return $selectors;
111
	}
112
113
	public function getXpath($css) {
114
		//$this->css = str_replace([' >', '> '],['>', '>'], trim($css));
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
115
		foreach ($css as $key => $token) {
116
			if ($token['type'] === Tokenizer::WHITESPACE &&
117
				(isset($css[$key+1]) && $css[$key+1]['type'] === Tokenizer::GREATER_THAN)) unset($css[$key]);
118
			else if ($token['type'] === Tokenizer::WHITESPACE &&
119
				(isset($css[$key-1]) && $css[$key-1]['type'] === Tokenizer::GREATER_THAN)) unset($css[$key]);
120
		}
121
		$css = $this->splitOnToken(array_values($css), Tokenizer::COLON)[0];
122
		$selectors = $this->split($css);
123
		$xpath = '/';
124
		foreach ($selectors as $selector) {
125
			if (isset($this->translators[$selector->type])) $xpath .= $this->translators[$selector->type]($selector->string, $xpath);
126
		}
127
128
		$xpath = str_replace('/[', '/*[', $xpath);
129
130
		return $xpath;
131
	}
132
133
	public function getDepth($css) {
134
		return count($this->split($css));
135
	}
136
137
	public function getPseudo($css) {
138
		$parts = $this->splitOnToken($css, Tokenizer::COLON);
139
		array_shift($parts);
140
		return $parts;
141
	}
142
}
143