public function testParseAnB() { // even $this->assertEquals(array(2, 0), Util::parseAnB('even')); // odd $this->assertEquals(array(2, 1), Util::parseAnB('odd')); // 5 $this->assertEquals(array(0, 5), Util::parseAnB('5')); // +5 $this->assertEquals(array(0, 5), Util::parseAnB('+5')); // n $this->assertEquals(array(1, 0), Util::parseAnB('n')); // 2n $this->assertEquals(array(2, 0), Util::parseAnB('2n')); // -234n $this->assertEquals(array(-234, 0), Util::parseAnB('-234n')); // -2n+1 $this->assertEquals(array(-2, 1), Util::parseAnB('-2n+1')); // -2n + 1 $this->assertEquals(array(-2, 1), Util::parseAnB(' -2n + 1 ')); // +2n-1 $this->assertEquals(array(2, -1), Util::parseAnB('2n-1')); $this->assertEquals(array(2, -1), Util::parseAnB('2n - 1')); // -n + 3 $this->assertEquals(array(-1, 3), Util::parseAnB('-n+3')); // Test invalid values $this->assertEquals(array(0, 0), Util::parseAnB('obviously + invalid')); }
/** * Provides functionality for all "An+B" rules. * Provides ntoh-child and also the functionality required for: * *- nth-last-child *- even *- odd *- first *- last *- eq *- nth *- nth-of-type *- first-of-type *- last-of-type *- nth-last-of-type * * See also QueryPath::CSS::DOMTraverser::Util::parseAnB(). */ protected function isNthChild($node, $value, $reverse = FALSE, $byType = FALSE) { list($groupSize, $elementInGroup) = Util::parseAnB($value); $parent = $node->parentNode; if (empty($parent) || $groupSize == 0 && $elementInGroup == 0 || $groupSize > 0 && $elementInGroup > $groupSize) { return FALSE; } // First we need to find the position of $node in other elements. if ($reverse) { $pos = $this->nodePositionFromEnd($node, $byType); } else { $pos = $this->nodePositionFromStart($node, $byType); } // If group size is 0, we just check to see if this // is the nth element: if ($groupSize == 0) { return $pos == $elementInGroup; } // Next, we normalize $elementInGroup if ($elementInGroup < 0) { $elementInGroup = $groupSize + $elementInGroup; } $prod = ($pos - $elementInGroup) / $groupSize; // fprintf(STDOUT, "%d n + %d on %d is %3.5f\n", $groupSize, $elementInGroup, $pos, $prod); return is_int($prod) && $prod >= 0; }
/** * Check to see if DOMNode has all of the given attributes. * * This can handle namespaced attributes, including namespace * wildcards. */ protected function matchAttributes($node, $attributes) { if (empty($attributes)) { return TRUE; } foreach ($attributes as $attr) { $val = isset($attr['value']) ? $attr['value'] : NULL; // Namespaced attributes. if (isset($attr['ns']) && $attr['ns'] != '*') { $nsuri = $node->lookupNamespaceURI($attr['ns']); if (empty($nsuri) || !$node->hasAttributeNS($nsuri, $attr['name'])) { return FALSE; } $matches = Util::matchesAttributeNS($node, $attr['name'], $nsuri, $val, $attr['op']); } elseif (isset($attr['ns']) && $attr['ns'] == '*' && $node->hasAttributes()) { // Cycle through all of the attributes in the node. Note that // these are DOMAttr objects. $matches = FALSE; $name = $attr['name']; foreach ($node->attributes as $attrNode) { if ($attrNode->localName == $name) { $nsuri = $attrNode->namespaceURI; $matches = Util::matchesAttributeNS($node, $name, $nsuri, $val, $attr['op']); } } } else { $matches = Util::matchesAttribute($node, $attr['name'], $val, $attr['op']); } if (!$matches) { return FALSE; } } return TRUE; }