AbstractXmlMiddleware::setCaseInsensitive()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2017–2019 Ryan Parman <http://ryanparman.com>.
4
 * Copyright (c) 2017–2019 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 5
    public function setCaseInsensitive(bool $makeInsensitive = true): self
39
    {
40 5
        $this->caseSensitive = !$makeInsensitive;
41
42 5
        return $this;
43
    }
44
45
    /**
46
     * Replace all instances of `%s` with the `$namespaceAlias` parameter.
47
     *
48
     * This is similar to `sprintf()`, but the `$namespaceAlias` is applied to _all_ instances of `%s`.
49
     *
50
     * @param string $query          An XPath query where `%s` is used in-place of the XML namespace alias.
51
     * @param string $namespaceAlias The XML namespace alias to apply.
52
     */
53
    public function applyNsToQuery(string $query, string $namespaceAlias): string
54
    {
55
        return \str_replace('%s', $namespaceAlias, $query);
56
    }
57
58
    /**
59
     * Produce an XPath 1.0 expression which is used to query XML document nodes.
60
     *
61
     * ```php
62
     * ['feed', 'entry', 5, 'id', '@xml:lang']
63
     * ```
64
     *
65
     * ```xpath
66
     * /feed/entry[5]/id/@xml:lang (simplified)
67
     * ```
68
     *
69
     * @param string $namespaceAlias The XML namespace alias to apply.
70
     * @param array  $path           An ordered array of nested elements, starting from the top-level XML node.
71
     *                               If an integer is added, then it is assumed that the element before it should be
72
     *                               handled as an array and the integer is its index. Expression is generated
73
     *                               left-to-right.
74
     *
75
     * @return string An XPath 1.0 expression.
76
     */
77 560
    public function generateQuery(string $namespaceAlias, array $path): string
78
    {
79 560
        $query = '';
80
81 560
        while (\count($path)) {
82 560
            $p    = \array_shift($path);
83 560
            $next = $path[0] ?? null;
84
85
            // Reduce to only the upper/lower of the active letters
86
            // ≈30-35% faster than the full alphabet
87 560
            $pLet  = \count_chars($p, 3);
88 560
            $pLow  = \mb_strtolower($pLet);
89 560
            $pUp   = \mb_strtoupper($pLet);
90 560
            $pAttr = ('@' === \mb_substr($p, 0, 1));
91
92 560
            if (\is_int($next)) {
93 220
                if ($this->caseSensitive || $pAttr) {
94
                    // case; next
95 215
                    $query .= \sprintf(
96 215
                        '/%s%s[position() = %d]',
97 215
                        (!$pAttr
98 215
                            ? $namespaceAlias . ':'
99 215
                            : ''),
100 215
                        $p,
101 215
                        \array_shift($path) + 1
102
                    );
103
                } else {
104
                    // icase; next
105 5
                    $query .= \sprintf(
106 5
                        '/%s*[translate(name(), \'%s\', \'%s\') = \'%s\'][position() = %d]',
107 5
                        (!$pAttr
108 5
                            ? $namespaceAlias . ':'
109 5
                            : ''),
110 5
                        $pUp,
111 5
                        $pLow,
112 5
                        $p,
113 220
                        \array_shift($path) + 1
114
                    );
115
                }
116
            } else {
117 560
                if ($this->caseSensitive || $pAttr) {
118
                    // case; no-next
119 560
                    $query .= \sprintf(
120 560
                        '/%s%s',
121 560
                        (!$pAttr
122 555
                            ? $namespaceAlias . ':'
123 560
                            : ''),
124 560
                        $p
125
                    );
126
                } else {
127
                    // icase; no-next
128 5
                    $query .= \sprintf(
129 5
                        '/%s*[translate(name(), \'%s\', \'%s\') = \'%s\']',
130 5
                        (!$pAttr
131 5
                            ? $namespaceAlias . ':'
132 5
                            : ''),
133 5
                        $pUp,
134 5
                        $pLow,
135 5
                        $p
136
                    );
137
                }
138
            }
139
        }
140
141 560
        return $query;
142
    }
143
}
144