These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | class CssToXpath { |
||
9 | private $specialChars = [Tokenizer::WHITESPACE, Tokenizer::DOT, Tokenizer::GREATER_THAN, |
||
10 | '~', Tokenizer::NUM_SIGN, Tokenizer::OPEN_SQUARE_BRACKET, Tokenizer::MULTIPLY]; |
||
11 | private $translators = []; |
||
12 | private static $instances = []; |
||
13 | private $functionSet; |
||
14 | private $id; |
||
15 | |||
16 | public function __construct(\Transphporm\FunctionSet $functionSet, $prefix = '', $id = null) { |
||
17 | $this->id = $id; |
||
18 | self::$instances[$this->id] = $this; |
||
19 | $this->functionSet = $functionSet; |
||
20 | |||
21 | $this->translators = [ |
||
22 | Tokenizer::WHITESPACE => function($string) use ($prefix) { return '//' . $prefix . $string; }, |
||
23 | Tokenizer::MULTIPLY => function () { return '*'; }, |
||
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) { return '[' .'php:function(\'\Transphporm\Parser\CssToXpath::processAttr\', \'' . base64_encode(serialize($string)) . '\', ., "' . $this->id . '")' . ']'; } |
||
29 | ]; |
||
30 | } |
||
31 | |||
32 | private function createSelector() { |
||
33 | $selector = new \stdclass; |
||
34 | $selector->type = ''; |
||
35 | $selector->string = ''; |
||
36 | return $selector; |
||
37 | } |
||
38 | |||
39 | //XPath only allows registering of static functions... this is a hacky workaround for that |
||
40 | public static function processAttr($attr, $element, $hash) { |
||
41 | $attr = unserialize(base64_decode($attr)); |
||
42 | |||
43 | $functionSet = self::$instances[$hash]->functionSet; |
||
44 | $functionSet->setElement($element[0]); |
||
45 | |||
46 | $attributes = []; |
||
47 | foreach($element[0]->attributes as $name => $node) { |
||
48 | $attributes[$name] = $node->nodeValue; |
||
49 | } |
||
50 | |||
51 | $parser = new \Transphporm\Parser\Value($functionSet, true); |
||
52 | $return = $parser->parseTokens($attr, $attributes); |
||
53 | return is_array($return[0]) || $return[0] === '' ? false : $return[0]; |
||
54 | } |
||
55 | |||
56 | public function cleanup() { |
||
57 | unset(self::$instances[$this->id]); |
||
58 | } |
||
59 | |||
60 | //split the css into indivudal functions |
||
61 | private function split($css) { |
||
62 | $selectors = []; |
||
63 | $selector = $this->createSelector(); |
||
64 | $selectors[] = $selector; |
||
65 | |||
66 | foreach ($css as $token) { |
||
67 | if (in_array($token['type'], $this->specialChars)) { |
||
68 | $selector = $this->createSelector(); |
||
69 | $selector->type = $token['type']; |
||
70 | $selectors[] = $selector; |
||
71 | } |
||
72 | if (isset($token['value'])) $selectors[count($selectors)-1]->string = $token['value']; |
||
73 | } |
||
74 | return $selectors; |
||
75 | } |
||
76 | |||
77 | public function getXpath($css) { |
||
78 | $css = $this->removeSpacesFromDirectDecend($css)->splitOnToken(Tokenizer::COLON)[0]->trim(); |
||
79 | $selectors = $this->split($css); |
||
80 | $xpath = '/'; |
||
81 | foreach ($selectors as $selector) { |
||
82 | if (isset($this->translators[$selector->type])) $xpath .= $this->translators[$selector->type]($selector->string, $xpath); |
||
83 | } |
||
84 | |||
85 | $xpath = str_replace('/[', '/*[', $xpath); |
||
86 | |||
87 | return $xpath; |
||
88 | } |
||
89 | |||
90 | private function removeSpacesFromDirectDecend($css) { |
||
91 | $tokens = new Tokens; |
||
92 | $split = $css->splitOnToken(Tokenizer::GREATER_THAN); |
||
93 | |||
94 | if (count($split) <= 1) return $css; |
||
95 | |||
96 | for ($i = 0; $i < count($split); $i++) { |
||
0 ignored issues
–
show
|
|||
97 | $tokens->add($split[$i]->trim()); |
||
98 | if (isset($split[$i+1])) $tokens->add(['type' => Tokenizer::GREATER_THAN]); |
||
99 | } |
||
100 | |||
101 | return $tokens; |
||
102 | } |
||
103 | |||
104 | |||
105 | public function getDepth($css) { |
||
106 | return count($this->split($css)); |
||
107 | } |
||
108 | |||
109 | public function getPseudo($css) { |
||
110 | $parts = $css->splitOnToken(Tokenizer::COLON); |
||
111 | array_shift($parts); |
||
112 | return $parts; |
||
113 | } |
||
114 | } |
||
115 |
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: