Completed
Push — master ( f4b721...596cf5 )
by Kacper
02:42
created

XPathQuery::query()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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