Issues (146)

src/CSS/DOMTraverser/Util.php (2 issues)

1
<?php
2
/**
3
 * @file
4
 *
5
 * Utilities for DOM traversal.
6
 */
7
8
namespace QueryPath\CSS\DOMTraverser;
9
10
use \QueryPath\CSS\EventHandler;
11
12
/**
13
 * Utilities for DOM Traversal.
14
 */
15
class Util
16
{
17
    /**
18
     * Check whether the given DOMElement has the given attribute.
19
     *
20
     * @param $node
21
     * @param $name
22
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
23
     * @param int $operation
24
     * @return bool
25
     */
26
    public static function matchesAttribute($node, $name, $value = NULL, $operation = EventHandler::IS_EXACTLY): bool
27
    {
28
        if (!$node->hasAttribute($name)) {
29
            return false;
30
        }
31
32
        if (NULL === $value) {
0 ignored issues
show
The condition NULL === $value is always true.
Loading history...
33
            return true;
34
        }
35
36
        return self::matchesAttributeValue($value, $node->getAttribute($name), $operation);
37
    }
38
39
    /**
40
     * Check whether the given DOMElement has the given namespaced attribute.
41
     */
42
    public static function matchesAttributeNS($node, $name, $nsuri, $value = NULL, $operation = EventHandler::IS_EXACTLY)
43
    {
44
        if (!$node->hasAttributeNS($nsuri, $name)) {
45
            return false;
46
        }
47
48
        if (is_null($value)) {
49
            return true;
50
        }
51
52
        return self::matchesAttributeValue($value, $node->getAttributeNS($nsuri, $name), $operation);
53
    }
54
55
    /**
56
     * Check for attr value matches based on an operation.
57
     */
58
    public static function matchesAttributeValue($needle, $haystack, $operation): bool
59
    {
60
61
        if (strlen($haystack) < strlen($needle)) {
62
            return false;
63
        }
64
65
        // According to the spec:
66
        // "The case-sensitivity of attribute names in selectors depends on the document language."
67
        // (6.3.2)
68
        // To which I say, "huh?". We assume case sensitivity.
69
        switch ($operation) {
70
            case EventHandler::IS_EXACTLY:
71
                return $needle == $haystack;
72
            case EventHandler::CONTAINS_WITH_SPACE:
73
                // XXX: This needs testing!
74
                return preg_match('/\b/', $haystack) == 1;
75
            //return in_array($needle, explode(' ', $haystack));
76
            case EventHandler::CONTAINS_WITH_HYPHEN:
77
                return in_array($needle, explode('-', $haystack));
78
            case EventHandler::CONTAINS_IN_STRING:
79
                return strpos($haystack, $needle) !== false;
80
            case EventHandler::BEGINS_WITH:
81
                return strpos($haystack, $needle) === 0;
82
            case EventHandler::ENDS_WITH:
83
                //return strrpos($haystack, $needle) === strlen($needle) - 1;
84
                return preg_match('/' . $needle . '$/', $haystack) == 1;
85
        }
86
87
        return false; // Shouldn't be able to get here.
88
    }
89
90
    /**
91
     * Remove leading and trailing quotes.
92
     */
93
    public static function removeQuotes(string $str)
94
    {
95
        $f = mb_substr($str, 0, 1);
96
        $l = mb_substr($str, -1);
97
        if ($f === $l && ($f === '"' || $f === "'")) {
98
            $str = mb_substr($str, 1, -1);
99
        }
100
101
        return $str;
102
    }
103
104
    /**
105
     * Parse an an+b rule for CSS pseudo-classes.
106
     *
107
     * Invalid rules return `array(0, 0)`. This is per the spec.
108
     *
109
     * @param $rule
110
     *  Some rule in the an+b format.
111
     * @retval array
112
     *  `array($aVal, $bVal)` of the two values.
113
     * @return array
114
     */
115
    public static function parseAnB($rule): array
116
    {
117
        if ($rule === 'even') {
118
            return [2, 0];
119
        }
120
121
        if ($rule === 'odd') {
122
            return [2, 1];
123
        }
124
125
        if ($rule === 'n') {
126
            return [1, 0];
127
        }
128
129
        if (is_numeric($rule)) {
130
            return [0, (int)$rule];
131
        }
132
133
        $regex = '/^\s*([+\-]?[0-9]*)n\s*([+\-]?)\s*([0-9]*)\s*$/';
134
        $matches = [];
135
        $res = preg_match($regex, $rule, $matches);
136
137
        // If it doesn't parse, return 0, 0.
138
        if (!$res) {
139
            return [0, 0];
140
        }
141
142
        $aVal = $matches[1] ?? 1;
143
        if ($aVal === '-') {
144
            $aVal = -1;
145
        } else {
146
            $aVal = (int)$aVal;
147
        }
148
149
        $bVal = 0;
150
        if (isset($matches[3])) {
151
            $bVal = (int)$matches[3];
152
            if (isset($matches[2]) && $matches[2] === '-') {
153
                $bVal *= -1;
154
            }
155
        }
156
157
        return [$aVal, $bVal];
158
    }
159
160
}
161