depage-xmldb v2.3.1
XML documents in a relational database
Loading...
Searching...
No Matches
XpathElement.php
Go to the documentation of this file.
1<?php
2
3namespace Depage\XmlDb;
4
5use Depage\XmlDb\Exceptions\XmlDbException;
6
18{
19 // Raw selector parts
20 public readonly string $divider;
21 public readonly string $namespace;
22 public readonly string $name;
23 public readonly string $condition;
24
25 // Pre-computed parsed conditions (set in __construct)
26 public readonly mixed $position; // false or ['op', 'value']
27 public readonly mixed $attributes; // array of condition-rows or false
28 public readonly bool $attributesValid; // true when condition matched attribute regex
29
36 public function __construct(string $divider, string $namespace, string $name, string $condition)
37 {
38 $this->divider = $divider;
39 $this->namespace = $namespace;
40 $this->name = $name;
41 $this->condition = $condition;
42
43 // Derive position from condition
44 if (is_string($condition)) {
45 $this->position = $this->parsePosition($condition);
46 } else {
47 $this->position = false;
48 }
49
50 // Derive attributes from condition
51 $attributesValid = false;
52 $attributes = false;
53 if ($condition !== '') {
54 $cleanString = $this->removeLiteralStrings($condition, $strings);
55 $validMatch = preg_match('/^[\w\d@=: -<>\*]*$/', $cleanString);
56 if ((bool) $validMatch) {
57 $attributesValid = true;
58 try {
59 $attributes = $this->getConditionAttributes($cleanString, $strings);
60 } catch (XmlDbException $e) {
61 $attributesValid = false;
62 $attributes = false;
63 }
64 }
65 }
66
67 $this->attributes = $attributes;
68 $this->attributesValid = $attributesValid;
69 }
70
75 protected function parsePosition($condition): array|false
76 {
77 $positionArray = [];
78 $matches = [];
79 $pOperator = '(=|!=|<|>|<=|>=)';
80 $pPosition = '([0-9]+)';
81
82 if (preg_match("/^\s*(?:(?:position\‍(\‍))\s*$pOperator)?\s*$pPosition\s*$/", $condition, $matches)) {
83 $positionArray[] = ($matches[1] == '') ? '=' : $matches[1];
84 $positionArray[] = $matches[2];
85 return $positionArray;
86 }
87 return false;
88 }
89
96 protected function getConditionAttributes($conditionString, $strings)
97 {
98 $conditionArray = [];
99 $pAttr = '@(\w[\w\d:]*)';
100 $pOperator = '(=|!=|<|>|<=|>=)';
101 $pBool = '(and|or|AND|OR)';
102 $pString = '\$(\d*)';
103
104 preg_match_all("/$pBool?\s*$pAttr\s*(?:$pOperator\s*$pString)?/", $conditionString, $conditions, PREG_SET_ORDER);
105
106 $first = true;
107 foreach ($conditions as $condition) {
108 $bool = $condition[1] ?? null;
109
110 if ($first === $bool) {
111 throw new XmlDbException('Invalid XPath syntax');
112 }
113 if ($first) {
114 $first = false;
115 }
116
117 $conditionArray[] = [
118 $condition[2],
119 $condition[3] ?? null,
120 (isset($condition[4]) && $condition[4] != '') ? $strings[$condition[4]] : null,
121 $bool,
122 ];
123 }
124 return $conditionArray;
125 }
126
132 protected function removeLiteralStrings($text, &$strings): string
133 {
134 $n = 0;
135 $newText = '';
136 $strings = [];
137
138 $p = "/([^\"']*)|(?:\"([^\"]*)\"|'([^']*)')/";
139 preg_match_all($p, $text, $parts);
140
141 for ($i = 0; $i < count($parts[0]); $i++) {
142 if ($parts[1][$i] == '' && ($parts[2][$i] != '' || $parts[3][$i] != '')) {
143 $strings[$n] = $parts[2][$i] . $parts[3][$i];
144 $newText .= "\$$n";
145 $n++;
146 } else {
147 $newText .= $parts[1][$i];
148 }
149 }
150 return $newText;
151 }
152}
153
154/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
A single parsed XPath element representing one node-selector in the path.
readonly string $condition
readonly string $namespace
removeLiteralStrings($text, &$strings)
getConditionAttributes($conditionString, $strings)
__construct(string $divider, string $namespace, string $name, string $condition)
readonly bool $attributesValid