dom = $dom; $this->xpath10 = new DOMXPath($this->dom); $this->result_cache_enabled = $enable_result_cache; $this->code_cache_enabled = $enable_code_cache; // Load the namespaces from the document. $node is a DOMNameSpaceNode so extract the prefix from the node name $this->nsMgr = new XmlNamespaceManager(); $xpath = new DOMXPath($this->dom); foreach ($xpath->query('namespace::*', $this->dom->documentElement) as $node) { $this->nsMgr->addNamespace(str_replace(array('xmlns', ':'), array('', ''), $node->nodeName), (string)$node->nodeValue); } // Create a navigator to navigate the document and move to root $this->navigator = new DOMXPathNavigator($this->dom, $this->nsMgr); $this->navigator->MoveToRoot(); } /** * Helper function to convert from (some) lyquidity types to native ones * * @param mixed $result * @return mixed */ protected function processResult($result) { if ($result instanceof FalseValue) return False; if ($result instanceof TrueValue) return True; return $result; } /** * Runs a XPath 2.0 query and returns an array of DomNodes * * @param String $query * @return array[DOMNode] */ public function runQuery(String $query): array { // Array di risultati $results = []; try { $xpath10results = @$this->xpath10->query($query); } catch (\Throwable $t) { $xpath10results = false; } if ($xpath10results !== false) { // Creiamo l'array di nodi foreach ($xpath10results as $node) { $results[] = $node; } } else { // Compile the XPath 2.0 $expression = null; if ($this->code_cache_enabled) { $expression = $this->code_cache[$query] ?? null; } if ($expression === null) { $expression = XPath2Expression::Compile($query, $this->nsMgr); if ($this->code_cache_enabled) $this->code_cache[$query] = $expression; } $provider = new NodeProvider($this->navigator); // Evaluate the expression $result = $expression->EvaluateWithVars($provider, null); if ($result) { // If there is a list of context then iterate over them. In this case $result will be ChildNodeIterator foreach ($result as /** @var DOMXPathNavigator $localNav */ $localNav) { $provider = new NodeProvider($localNav); $domNode = $localNav->getUnderlyingObject(); $results[] = $domNode; } } } // Return results return $results; } /** * Runs a XPath 2.0 query on a XPath 2.0 context query * * @param String $context_query * @param String $query * @return array */ public function runQueryContext(String $context_query, String $query): array { if ($this->result_cache_enabled && isset($this->result_cache[$context_query]) && isset($this->result_cache[$context_query][$query])) { return $this->result_cache[$context_query][$query]; } $results = []; $context = $this->runQuery($context_query); if (!empty($context)) { // If there is a list of context then iterate over them. In this case $result will be ChildNodeIterator foreach ($context as /** @var DOMNode $domNode */ $domNode) { $localNav = new DOMXPathNavigator($domNode); $provider = new NodeProvider($localNav); // Compile the XPath 2.0 $expression = null; if ($this->code_cache_enabled) { $expression = $this->code_cache[$query] ?? null; } if ($expression === null) { $expression = XPath2Expression::Compile($query, $this->nsMgr); if ($this->code_cache_enabled) $this->code_cache[$query] = $expression; } // Evaluate it in the current context $result = $expression->EvaluateWithVars($provider, null); // Organize the results in a simple to use array $results[] = $this->processResult($result); } } // Return results if ($this->result_cache_enabled) $this->result_cache[$context_query][$query] = $results; return $results; } public const XPATH_FAIL_EXCEPTION = 1; public const XPATH_FAIL_NO_RESULTS = 2; public const XPATH_FAIL_NOT_ENOUGH_RESULTS = 3; public const XPATH_FAIL_WRONG_TYPE = 4; /** * Will evaluate an XPath 2.0 condition to True/False and fail gracefully * * @param string $query * @param string||null $context If not null, manually provide a context to execute the query * @return array||null The value of the evaluation */ public function evalBoolean($query, $context, $index, &$error): ?bool { // Evaluate the condition try { $condition_results = $this->runQueryContext($context, $query); } catch (XPath2Exception $xpath_ex) { // TODO Un giorno magari le debugghiamo $error = self::XPATH_FAIL_EXCEPTION; return null; } catch (\Throwable $ex) { throw $ex; $error = self::XPATH_FAIL_EXCEPTION; return null; } // Get count of results $cr_count = count($condition_results); // No results if ($cr_count === 0) { $error = self::XPATH_FAIL_NO_RESULTS; return null; } if ($index >= $cr_count) { $error = self::XPATH_FAIL_NOT_ENOUGH_RESULTS; return null; } $condition_result = $condition_results[$index]; // Not a boolean if (!is_bool($condition_result)) { $error = self::XPATH_FAIL_WRONG_TYPE; throw new Exception("Wrong type, boolean expected."); return null; } return $condition_result; } }