Completed
Push — master ( 71fb31...3969f4 )
by Ryan
03:21
created

AbstractXmlMiddleware   A

Complexity

Total Complexity 7

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 117
rs 10
c 0
b 0
f 0
wmc 7

3 Methods

Rating   Name   Duplication   Size   Complexity  
A setCaseInsensitive() 0 5 1
A applyNsToQuery() 0 3 1
B generateQuery() 0 56 5
1
<?php
2
/**
3
 * Copyright (c) 2017–2018 Ryan Parman <http://ryanparman.com>.
4
 * Copyright (c) 2017–2018 Contributors.
5
 *
6
 * http://opensource.org/licenses/Apache2.0
7
 */
8
9
declare(strict_types=1);
10
11
namespace SimplePie\Middleware\Xml;
12
13
use SimplePie\Middleware\AbstractMiddleware;
14
use SimplePie\Type\Node;
15
16
/**
17
 * The base XML middleware class that all other XML middleware classes extend from. It handles low-level functionality
18
 * that is shared across all XML middleware classes.
19
 */
20
abstract class AbstractXmlMiddleware extends AbstractMiddleware
21
{
22
    /**
23
     * The status of case-sensitivity.
24
     *
25
     * @var bool
26
     */
27
    protected $caseSensitive = true;
28
29
    /**
30
     * By default, SimplePie NG is case-sensitive (as per the specification). If an invalid feed is parsed that does not
31
     * follow the specification with regard to casing of XML elements, this method allows you to trade some performance
32
     * in favor of case-insensitive parsing.
33
     *
34
     * @param bool $makeInsensitive Whether or not the handling should be made case-insensitive. A value of `true`
35
     *                              means that the handling should be case-insensitive. A value of `false` means that
36
     *                              the handling should be case-sensitive. The default value is `true`.
37
     *
38
     * @return self
39
     */
40
    public function setCaseInsensitive(bool $makeInsensitive = true): self
41
    {
42
        $this->caseSensitive = !$makeInsensitive;
43
44
        return $this;
45
    }
46
47
    /**
48
     * Replace all instances of `%s` with the `$namespaceAlias` parameter.
49
     *
50
     * This is similar to `sprintf()`, but the `$namespaceAlias` is applied to _all_ instances of `%s`.
51
     *
52
     * @param string $query          An XPath query where `%s` is used in-place of the XML namespace alias.
53
     * @param string $namespaceAlias The XML namespace alias to apply.
54
     *
55
     * @return string
56
     */
57
    public function applyNsToQuery(string $query, string $namespaceAlias): string
58
    {
59
        return \str_replace('%s', $namespaceAlias, $query);
60
    }
61
62
    /**
63
     * Produce an XPath 1.0 expression which is used to query XML document nodes.
64
     *
65
     * ```php
66
     * ['feed', 'entry', 5, 'id']
67
     * ```
68
     *
69
     * ```xpath
70
     * /feed/entry[5]/id (simplified)
71
     * ```
72
     *
73
     * @param string $namespaceAlias The XML namespace alias to apply.
74
     * @param array  $path           An ordered array of nested elements, starting from the top-level XML node.
75
     *                               If an integer is added, then it is assumed that the element before it should be
76
     *                               handled as an array and the integer is its index. Expression is generated
77
     *                               left-to-right.
78
     *
79
     * @return string An XPath 1.0 expression.
80
     */
81
    public function generateQuery(string $namespaceAlias, array $path): string
82
    {
83
        $query = '';
84
85
        while (\count($path)) {
86
            $p    = \array_shift($path);
87
            $next = $path[0] ?? null;
88
89
            // Reduce to only the upper/lower of the active letters
90
            // ≈30-35% faster than the full alphabet
91
            $pLet = \count_chars($p, 3);
92
            $pLow = \mb_strtolower($pLet);
93
            $pUp  = \mb_strtoupper($pLet);
94
95
            if (\is_int($next)) {
96
                if ($this->caseSensitive) {
97
                    // case; next
98
                    $query .= \sprintf(
99
                        '/%s:%s[position() = %d]',
100
                        $namespaceAlias,
101
                        $p,
102
                        \array_shift($path) + 1
103
                    );
104
                } else {
105
                    // icase; next
106
                    $query .= \sprintf(
107
                        '/%s:*[translate(name(), \'%s\', \'%s\') = \'%s\'][position() = %d]',
108
                        $namespaceAlias,
109
                        $pUp,
110
                        $pLow,
111
                        $p,
112
                        \array_shift($path) + 1
113
                    );
114
                }
115
            } else {
116
                if ($this->caseSensitive) {
117
                    // case; no-next
118
                    $query .= \sprintf(
119
                        '/%s:%s',
120
                        $namespaceAlias,
121
                        $p
122
                    );
123
                } else {
124
                    // icase; no-next
125
                    $query .= \sprintf(
126
                        '/%s:*[translate(name(), \'%s\', \'%s\') = \'%s\']',
127
                        $namespaceAlias,
128
                        $pUp,
129
                        $pLow,
130
                        $p
131
                    );
132
                }
133
            }
134
        }
135
136
        return $query;
137
    }
138
}
139