summaryrefslogtreecommitdiff
path: root/lib/querypath/src/QueryPath/CSS/DOMTraverser/Util.php
blob: ec01d8f09c223dc3e1ab978e5730383ed2dcf83e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?php
/**
 * @file
 *
 * Utilities for DOM traversal.
 */
namespace QueryPath\CSS\DOMTraverser;

use \QueryPath\CSS\EventHandler;

/**
 * Utilities for DOM Traversal.
 */
class Util {

  /**
   * Check whether the given DOMElement has the given attribute.
   */
  public static function matchesAttribute($node, $name, $value = NULL, $operation = EventHandler::isExactly) {
    if (!$node->hasAttribute($name)) {
      return FALSE;
    }

    if (is_null($value)) {
      return TRUE;
    }

    return self::matchesAttributeValue($value, $node->getAttribute($name), $operation);
  }
  /**
   * Check whether the given DOMElement has the given namespaced attribute.
   */
  public static function matchesAttributeNS($node, $name, $nsuri, $value = NULL, $operation = EventHandler::isExactly) {
    if (!$node->hasAttributeNS($nsuri, $name)) {
      return FALSE;
    }

    if (is_null($value)) {
      return TRUE;
    }

    return self::matchesAttributeValue($value, $node->getAttributeNS($nsuri, $name), $operation);
  }

  /**
   * Check for attr value matches based on an operation.
   */
  public static function matchesAttributeValue($needle, $haystack, $operation) {

    if (strlen($haystack) < strlen($needle)) return FALSE;

    // According to the spec:
    // "The case-sensitivity of attribute names in selectors depends on the document language."
    // (6.3.2)
    // To which I say, "huh?". We assume case sensitivity.
    switch ($operation) {
      case EventHandler::isExactly:
        return $needle == $haystack;
      case EventHandler::containsWithSpace:
        // XXX: This needs testing!
        return preg_match('/\b/', $haystack) == 1;
        //return in_array($needle, explode(' ', $haystack));
      case EventHandler::containsWithHyphen:
        return in_array($needle, explode('-', $haystack));
      case EventHandler::containsInString:
        return strpos($haystack, $needle) !== FALSE;
      case EventHandler::beginsWith:
        return strpos($haystack, $needle) === 0;
      case EventHandler::endsWith:
        //return strrpos($haystack, $needle) === strlen($needle) - 1;
        return preg_match('/' . $needle . '$/', $haystack) == 1;
    }
    return FALSE; // Shouldn't be able to get here.
  }

  /**
   * Remove leading and trailing quotes.
   */
  public static function removeQuotes($str) {
    $f = substr($str, 0, 1);
    $l = substr($str, -1);
    if ($f === $l && ($f == '"' || $f == "'")) {
      $str = substr($str, 1, -1);
    }
    return $str;
  }

  /**
   * Parse an an+b rule for CSS pseudo-classes.
   *
   * Invalid rules return `array(0, 0)`. This is per the spec.
   *
   * @param $rule
   *  Some rule in the an+b format.
   * @retval array
   *  `array($aVal, $bVal)` of the two values.
   */
  public static function parseAnB($rule) {
    if ($rule == 'even') {
      return array(2, 0);
    }
    elseif ($rule == 'odd') {
      return array(2, 1);
    }
    elseif ($rule == 'n') {
      return array(1, 0);
    }
    elseif (is_numeric($rule)) {
      return array(0, (int)$rule);
    }

    $regex = '/^\s*([+\-]?[0-9]*)n\s*([+\-]?)\s*([0-9]*)\s*$/';
    $matches = array();
    $res = preg_match($regex, $rule, $matches);

    // If it doesn't parse, return 0, 0.
    if (!$res) {
      return array(0, 0);
    }

    $aVal = isset($matches[1]) ? $matches[1] : 1;
    if ($aVal == '-') {
      $aVal = -1;
    }
    else {
      $aVal = (int) $aVal;
    }

    $bVal = 0;
    if (isset($matches[3])) {
      $bVal = (int) $matches[3];
      if (isset($matches[2]) && $matches[2] == '-') {
        $bVal *= -1;
      }
    }
    return array($aVal, $bVal);
  }

}