XPathQuery::getElementFromPath()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 5
nop 1
dl 0
loc 21
ccs 0
cts 10
cp 0
crap 20
rs 9.0534
c 0
b 0
f 0
1
<?php
2
/**
3
 * Nucleus - XMPP Library for PHP
4
 *
5
 * Copyright (C) 2016, Some rights reserved.
6
 *
7
 * @author Kacper "Kadet" Donat <[email protected]>
8
 *
9
 * Contact with author:
10
 * Xmpp: [email protected]
11
 * E-mail: [email protected]
12
 *
13
 * From Kadet with love.
14
 */
15
16
namespace Kadet\Xmpp\Xml;
17
18
class XPathQuery
19
{
20
    private $_query;
21
    /** @var \DOMXPath */
22
    private $_xpath;
23
    private $_context;
24
25
    public function with(string $prefix, string $namespace)
26
    {
27
        $this->_xpath->registerNamespace($prefix, $namespace);
28
29
        return $this;
30
    }
31
32
    public function query(string $query = null)
33
    {
34
        /** @var \DOMNode $element */
35
        foreach ($this->_xpath->query($query ?: $this->_query) as $element) {
36
            yield $this->getElementFromPath($element->getNodePath());
37
        }
38
    }
39
40
    public function evaluate(string $query = null)
41
    {
42
        return $this->_xpath->evaluate($query ?: $this->_query);
43
    }
44
45
    public function __construct(string $query, XmlElement $context)
46
    {
47
        $document = new \DOMDocument();
48
        $document->loadXML($context->xml(false));
49
        $this->_xpath   = new \DOMXPath($document);
50
        $this->with('php', 'http://php.net/xpath');
51
52
        $this->_query   = $query;
53
        $this->_context = $context;
54
    }
55
56
    /**
57
     * Hack for supporting XPath outside of standard XML implementation.
58
     * Why? Because simplexml sucks so much.
59
     * DOM sucks even more, and I'm better with writing one hack for xpath
60
     * than with writing hack for almost everyfuckingthingâ„¢.
61
     *
62
     * @param $path
63
     * @return false|XmlElement
64
     */
65
    private function getElementFromPath($path)
66
    {
67
        // Split path into pieces and remove the first one (it's current node)
68
        $path = explode('/', trim($path, '/'));
69
        array_shift($path);
70
71
        $current = $this->_context;
72
        foreach ($path as $chunk) {
73
            // Chunk is in format node-name[index], parse it with regex
74
            preg_match('/([\w\*]+)(?:\[([0-9]+)\])?/', $chunk, $matches);
75
76
            $name  = $matches[1];
77
            $index = isset($matches[2]) ? $matches[2] - 1 : 0;
78
79
            // Path returns * if namespace occurs so we need to obtain index-th child
80
            // in other case we have to obtain index-th child with $name name
81
            $current = $name == '*' ? $current->children[$index] : $current->element($name, null, $index);
82
        }
83
84
        return $current;
85
    }
86
}
87