depage-xmldb v2.3.1
XML documents in a relational database
Loading...
Searching...
No Matches
XpathParser.php
Go to the documentation of this file.
1<?php
2
3namespace Depage\XmlDb;
4
5use Depage\XmlDb\Exceptions\XmlDbException;
6
16{
21 public function parseXpathElements(string $xpath): array
22 {
23 $pName = '(?:([^\/\‍[\‍]]*):)?([^\/\‍[\‍]]+)';
24 $pCondition = '(?:\‍[(.*?)\‍])?';
25 preg_match_all("/(\/+)$pName$pCondition/", $xpath, $raw, PREG_SET_ORDER);
26
27 $elements = [];
28 foreach ($raw as $match) {
29 $elements[] = new XpathElement(
30 $match[1] ?? '', // divider
31 $match[2] ?? '', // namespace
32 $match[3] ?? '', // name
33 $match[4] ?? '' // condition
34 );
35 }
36 return $elements;
37 }
38
43 public function parsePosition($condition): array|false
44 {
45 $positionArray = [];
46 $matches = [];
47 $pOperator = '(=|!=|<|>|<=|>=)';
48 $pPosition = '([0-9]+)';
49
50 if (preg_match("/^\s*(?:(?:position\‍(\‍))\s*$pOperator)?\s*$pPosition\s*$/", $condition, $matches)) {
51 $positionArray[] = ($matches[1] == '') ? '=' : $matches[1];
52 $positionArray[] = $matches[2];
53 return $positionArray;
54 }
55 return false;
56 }
57
62 public function parseAttributes($condition): false|array
63 {
64 $condArray = false;
65 $tempCondition = $this->removeLiteralStrings($condition, $strings);
66
67 if (preg_match('/^[\w\d@=: -<>\*]*$/', $tempCondition)) {
68 $condArray = $this->getConditionAttributes($tempCondition, $strings);
69 }
70 return $condArray;
71 }
72
78 public function translateName($ns, $name): string
79 {
80 $colon = (strlen($ns) && strlen($name)) ? ':' : '';
81 return str_replace('*', '%', "$ns$colon$name");
82 }
83
89 public function getConditionOperator($ns, $name): string
90 {
91 if (str_contains($ns, "*") || str_contains($name, "*")) {
92 return 'LIKE';
93 } else {
94 return '=';
95 }
96 }
97
103 public function getConditionAttributes($conditionString, $strings): array
104 {
105 $conditionArray = [];
106 $pAttr = '@(\w[\w\d:]*)';
107 $pOperator = '(=|!=|<|>|<=|>=)';
108 $pBool = '([^@\s]+)';
109 $pString = '\$(\d*)';
110
111 $matched = preg_match_all("/$pBool?\s*$pAttr\s*(?:$pOperator\s*$pString)?/", $conditionString, $conditions, PREG_SET_ORDER);
112
113 $first = true;
114 foreach ($conditions as $condition) {
115 $bool = $condition[1] ? strtolower($condition[1]) : null;
116 if ($first && $bool) {
117 throw new XmlDbException('Invalid XPath syntax');
118 }
119 if (!in_array($bool, ['and', 'or', null])) {
120 throw new XmlDbException('Invalid XPath syntax');
121 }
122
123 if ($first) {
124 $first = false;
125 }
126
127 $conditionArray[] = [
128 $condition[2],
129 $condition[3] ?? null,
130 (isset($condition[4]) && $condition[4] != '') ? $strings[$condition[4]] : null,
131 $bool,
132 ];
133 }
134 return $conditionArray;
135 }
136
142 protected function removeLiteralStrings($text, &$strings): string
143 {
144 $n = 0;
145 $newText = '';
146 $strings = [];
147
148 $p = "/([^\"']*)|(?:\"([^\"]*)\"|'([^']*)')/";
149 preg_match_all($p, $text, $parts);
150
151 for ($i = 0; $i < count($parts[0]); $i++) {
152 if ($parts[1][$i] == '' && ($parts[2][$i] != '' || $parts[3][$i] != '')) {
153 $strings[$n] = $parts[2][$i] . $parts[3][$i];
154 $newText .= "\$$n";
155 $n++;
156 } else {
157 $newText .= $parts[1][$i];
158 }
159 }
160 return $newText;
161 }
162
168 public function cleanOperator($operator): string
169 {
170 $operator = strtolower(trim($operator));
171 $cleaned = '';
172 $operators = ['=', '!=', '<=', '>=', '<', '>', 'and', 'or'];
173
174 if (in_array($operator, $operators)) {
175 $cleaned = $operator;
176 } else {
177 throw new XmlDbException("Invalid XPath operator \"$operator\"");
178 }
179 return $cleaned;
180 }
181}
182
183/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
A single parsed XPath element representing one node-selector in the path.
Parses XPath strings into XpathElement objects.
parseXpathElements(string $xpath)
removeLiteralStrings($text, &$strings)
getConditionAttributes($conditionString, $strings)
getConditionOperator($ns, $name)