Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

DOMQuery now acts like jQuery.

  • Loading branch information...
commit 83ded34eaf40d081e3c1dd8f3c30c70fe04b0f23 1 parent a2bfb27
Matt Butcher authored
Showing with 146 additions and 139 deletions.
  1. +106 −122 src/QueryPath/DOMQuery.php
  2. +40 −17 test/Tests/QueryPath/DOMQueryTest.php
228 src/QueryPath/DOMQuery.php
View
@@ -119,8 +119,19 @@ public function __construct($document = NULL, $string = NULL, $options = array()
// Figure out if document is DOM, HTML/XML, or a filename
elseif (is_object($document)) {
- if ($document instanceof DOMQuery) {
- $this->matches = $document->get(NULL, TRUE);
+ // This is the most frequent object type.
+ if ($document instanceof \SplObjectStorage) {
+ $this->matches = $document;
+ if ($document->count() != 0) {
+ $first = $this->getFirstMatch();
+ if (!empty($first->ownerDocument)) {
+ $this->document = $first->ownerDocument;
+ }
+ }
+ }
+ elseif ($document instanceof DOMQuery) {
+ //$this->matches = $document->get(NULL, TRUE);
+ $this->setMatches($document->get(NULL, TRUE));
if ($this->matches->count() > 0)
$this->document = $this->getFirstMatch()->ownerDocument;
}
@@ -140,13 +151,6 @@ public function __construct($document = NULL, $string = NULL, $options = array()
//$this->matches = array($import);
$this->setMatches($import);
}
- elseif ($document instanceof \SplObjectStorage) {
- if ($document->count() == 0) {
- throw new \QueryPath\Exception('Cannot initialize QueryPath from an empty SplObjectStore');
- }
- $this->matches = $document;
- $this->document = $this->getFirstMatch()->ownerDocument;
- }
else {
throw new \QueryPath\Exception('Unsupported class type: ' . get_class($document));
}
@@ -184,7 +188,12 @@ public function __construct($document = NULL, $string = NULL, $options = array()
// Do a find if the second param was set.
if (isset($string) && strlen($string) > 0) {
- $this->find($string);
+ // We don't issue a find because that creates a new DOMQuery.
+ //$this->find($string);
+
+ $query = new \QueryPath\CSS\DOMTraverser($this->matches);
+ $query->find($string);
+ $this->setMatches($query->matches());
}
}
@@ -240,8 +249,9 @@ public function getOptions() {
* for the current document.
*/
public function top($selector = NULL) {
- $this->setMatches($this->document->documentElement);
- return !empty($selector) ? $this->find($selector) : $this;
+ //$this->setMatches($this->document->documentElement);
+ //return !empty($selector) ? $this->find($selector) : $this;
+ return $this->inst($this->document->documentElement, $selector, $this->options);
}
/**
@@ -264,6 +274,13 @@ public function find($selector) {
//$query = new QueryPathEventHandler($this->matches);
$query = new \QueryPath\CSS\DOMTraverser($this->matches);
$query->find($selector);
+ //$this->setMatches($query->matches());
+ //return $this;
+ return $this->inst($query->matches(), NULL , $this->options);
+ }
+ public function findInPlace($selector) {
+ $query = new \QueryPath\CSS\DOMTraverser($this->matches);
+ $query->find($selector);
$this->setMatches($query->matches());
return $this;
}
@@ -309,8 +326,9 @@ public function xpath($query, $options = array()) {
for ($i = 0; $i < $nl->length; ++$i) $found->attach($nl->item($i));
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
+ //$this->setMatches($found);
+ //return $this;
}
/**
@@ -693,9 +711,10 @@ public function removeAttr($name) {
* @see end()
*/
public function eq($index) {
+ return $this->inst($this->getNthMatch($index), NULL, $this->options);
// XXX: Might there be a more efficient way of doing this?
- $this->setMatches($this->getNthMatch($index));
- return $this;
+ //$this->setMatches($this->getNthMatch($index));
+ //return $this;
}
/**
* Given a selector, this checks to see if the current set has one or more matches.
@@ -737,6 +756,7 @@ public function is($selector) {
}
// Testing based on Issue #70.
+ //fprintf(STDOUT, __FUNCTION__ .' found %d', $this->find($selector)->count());
return $this->branch($selector)->count() > 0;
// Old version:
@@ -765,9 +785,16 @@ public function is($selector) {
*/
public function filter($selector) {
$found = new \SplObjectStorage();
- foreach ($this->matches as $m) if (QueryPath::with($m, NULL, $this->options)->is($selector)) $found->attach($m);
- $this->setMatches($found);
- return $this;
+ foreach ($this->matches as $m) {
+ if (QueryPath::with($m, NULL, $this->options)->is($selector)) {
+ //fprintf(STDOUT, 'Attaching %s for %s', $m->tagName, $selector);
+ $found->attach($m);
+ }
+ }
+
+ return $this->inst($found, NULL, $this->options);
+ //$this->setMatches($found);
+ //return $this;
}
/**
* Sort the contents of the QueryPath object.
@@ -844,7 +871,7 @@ public function sort($comparator, $modifyDOM = FALSE) {
foreach ($list as $node) {
$found->attach($node);
}
- $this->setMatches($found);
+ //$this->setMatches($found);
// Do DOM modifications only if necessary.
@@ -860,7 +887,7 @@ public function sort($comparator, $modifyDOM = FALSE) {
$placeholder->parentNode->removeChild($placeholder);
}
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Filter based on a lambda function.
@@ -895,8 +922,7 @@ public function filterLambda($fn) {
foreach ($this->matches as $item)
if ($function($i++, $item) !== FALSE) $found->attach($item);
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -942,9 +968,7 @@ public function filterPreg($regex) {
$found->attach($item);
}
}
- $this->setMatches($found);
-
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Filter based on a callback function.
@@ -983,8 +1007,7 @@ public function filterCallback($callback) {
else {
throw new \QueryPath\Exception('The specified callback is not callable.');
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Filter a list to contain only items that do NOT match.
@@ -1012,8 +1035,7 @@ public function not($selector) {
else {
foreach ($this->matches as $m) if (!QueryPath::with($m, NULL, $this->options)->is($selector)) $found->attach($m);
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get an item's index.
@@ -1102,8 +1124,7 @@ public function map($callback) {
else {
throw new \QueryPath\Exception('Callback is not callable.');
}
- $this->setMatches($found, FALSE);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Narrow the items in this object down to only a slice of the starting items.
@@ -1121,8 +1142,7 @@ public function slice($start, $length = 0) {
$end = $length;
$found = new \SplObjectStorage();
if ($start >= $this->size()) {
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
$i = $j = 0;
@@ -1137,8 +1157,7 @@ public function slice($start, $length = 0) {
++$i;
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Run a callback on each item in the list of items.
@@ -1440,8 +1459,7 @@ public function replaceWith($new) {
$parent->insertBefore($data->cloneNode(TRUE), $m);
$found->attach($parent->removeChild($m));
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Remove the parent element from the selected node or nodes.
@@ -1671,8 +1689,7 @@ public function deepest() {
$winner->attach($lele);
}
}
- $this->setMatches($winner);
- return $this;
+ return $this->inst($winner, NULL, $this->options);
}
/**
@@ -1895,6 +1912,8 @@ public function add($selector) {
/**
* Revert to the previous set of matches.
*
+ * <b>DEPRECATED</b> Do not use.
+ *
* This will revert back to the last set of matches (before the last
* "destructive" set of operations). This undoes any change made to the set of
* matched objects. Functions like find() and filter() change the
@@ -1924,12 +1943,13 @@ public function add($selector) {
* operation.
* @see andSelf()
* @see add()
+ * @deprecated This function will be removed.
*/
public function end() {
// Note that this does not use setMatches because it must set the previous
// set of matches to empty array.
- $this->matches = $this->last;
- $this->last = new \SplObjectStorage();
+ //$this->matches = $this->last;
+ //$this->last = new \SplObjectStorage();
return $this;
}
/**
@@ -2002,14 +2022,11 @@ public function children($selector = NULL) {
if ($c->nodeType == XML_ELEMENT_NODE) $found->attach($c);
}
}
- if (empty($selector)) {
- $this->setMatches($found);
- }
- else {
- $this->matches = $found; // Don't buffer this. It is temporary.
- $this->filter($selector);
+ $new = $this->inst($found, NULL, $this->options);
+ if (!empty($selector)) {
+ return $new->filter($selector);
}
- return $this;
+ return $new;
}
/**
* Get all child nodes (not just elements) of all items in the matched set.
@@ -2037,8 +2054,7 @@ public function contents() {
$found->attach($c);
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get a list of siblings for elements currently wrapped by this object.
@@ -2073,13 +2089,11 @@ public function siblings($selector = NULL) {
}
}
if (empty($selector)) {
- $this->setMatches($found);
+ return $this->inst($found, NULL, $this->options);
}
else {
- $this->matches = $found; // Don't buffer this. It is temporary.
- $this->filter($selector);
+ return $this->inst($found, NULL, $this->options)->filter($selector);
}
- return $this;
}
/**
* Find the closest element matching the selector.
@@ -2114,8 +2128,10 @@ public function closest($selector) {
}
}
- $this->setMatches($found);
- return $this;
+ // XXX: Should this be an in-place modification?
+ return $this->inst($found, NULL, $this->options);
+ //$this->setMatches($found);
+ //return $this;
}
/**
* Get the immediate parent of each element in the DOMQuery.
@@ -2151,8 +2167,7 @@ public function parent($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get all ancestors of each element in the DOMQuery.
@@ -2183,8 +2198,7 @@ public function parents($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Set or get the markup for an element.
@@ -2789,8 +2803,7 @@ public function next($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get all siblings after an element.
@@ -2825,8 +2838,7 @@ public function nextAll($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get the next sibling before each element in the DOMQuery.
@@ -2864,8 +2876,7 @@ public function prev($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Get the previous siblings for each element in the DOMQuery.
@@ -2900,30 +2911,7 @@ public function prevAll($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
- }
- /**
- * @deprecated Use {@link siblings()}.
- */
- public function peers($selector = NULL) {
- $found = new \SplObjectStorage();
- foreach ($this->matches as $m) {
- foreach ($m->parentNode->childNodes as $kid) {
- if ($kid->nodeType == XML_ELEMENT_NODE && $m !== $kid) {
- if (!empty($selector)) {
- if (QueryPath::with($kid, NULL, $this->options)->is($selector)) {
- $found->attach($kid);
- }
- }
- else {
- $found->attach($kid);
- }
- }
- }
- }
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
* Add a class to all elements in the current DOMQuery.
@@ -3100,12 +3088,24 @@ public function hasClass($class) {
*/
public function branch($selector = NULL) {
$temp = \QueryPath::with($this->matches, NULL, $this->options);
- if (isset($selector)) $temp->find($selector);
+ //if (isset($selector)) $temp->find($selector);
+ $temp->document = $this->document;
+ if (isset($selector)) $temp->findInPlace($selector);
+ return $temp;
+ }
+ protected function inst($matches, $selector, $options) {
+ $temp = \QueryPath::with($matches, NULL, $options);
+ //if (isset($selector)) $temp->find($selector);
+ $temp->document = $this->document;
+ if (isset($selector)) $temp->findInPlace($selector);
return $temp;
}
/**
* Perform a deep clone of each node in the DOMQuery.
*
+ * @attention
+ * This is an in-place modification of the current QueryPath object.
+ *
* This does not clone the DOMQuery object, but instead clones the
* list of nodes wrapped by the DOMQuery. Every element is deeply
* cloned.
@@ -3120,7 +3120,8 @@ public function branch($selector = NULL) {
public function cloneAll() {
$found = new \SplObjectStorage();
foreach ($this->matches as $m) $found->attach($m->cloneNode(TRUE));
- $this->setMatches($found, FALSE);
+ //return $this->inst($found, NULL, $this->options);
+ $this->setMatches($found);
return $this;
}
@@ -3142,7 +3143,7 @@ public function __clone() {
/**
* Detach any items from the list if they match the selector.
*
- * In other words, each item that matches the selector will be remove
+ * In other words, each item that matches the selector will be removed
* from the DOM document. The returned DOMQuery wraps the list of
* removed elements.
*
@@ -3171,8 +3172,7 @@ public function detach($selector = NULL) {
// the one passed in, so we have to re-store it.
$found->attach($item->parentNode->removeChild($item));
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3247,8 +3247,7 @@ public function has($contained) {
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3292,9 +3291,7 @@ public function even() {
if ($even && $m->nodeType == XML_ELEMENT_NODE) $found->attach($m);
$even = ($even) ? false : true;
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3319,9 +3316,7 @@ public function odd() {
if ($odd && $m->nodeType == XML_ELEMENT_NODE) $found->attach($m);
$odd = ($odd) ? false : true;
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3343,9 +3338,7 @@ public function first() {
break;
}
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3373,9 +3366,7 @@ public function firstChild() {
}
if($flag) break;
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3400,9 +3391,7 @@ public function last() {
if ($item) {
$found->attach($item);
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3430,9 +3419,7 @@ public function lastChild() {
$item = null;
}
}
- $this->setMatches($found);
- $this->matches = $found; // Don't buffer this. It is temporary.
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3473,8 +3460,7 @@ public function nextUntil($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3509,8 +3495,7 @@ public function prevUntil($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/**
@@ -3546,8 +3531,7 @@ public function parentsUntil($selector = NULL) {
}
}
}
- $this->setMatches($found);
- return $this;
+ return $this->inst($found, NULL, $this->options);
}
/////// INTERNAL FUNCTIONS ////////
57 test/Tests/QueryPath/DOMQueryTest.php
View
@@ -322,6 +322,24 @@ public function testFind() {
$this->assertEquals(1, count($qp->get()), 'Check class.');
$this->assertEquals($qp->get(0)->tagName, 'root');
}
+ public function testFindInPlace() {
+ $file = DATA_FILE;
+ $qp = qp($file)->find('#head');
+ $this->assertEquals(1, count($qp->get()));
+ $this->assertEquals($qp->get(0)->tagName, 'head');
+
+ $this->assertEquals('inner', qp($file)->find('.innerClass')->tag());
+
+ $string = '<?xml version="1.0"?><root><a/>Test</root>';
+ $qp = qp($string)->find('root');
+ $this->assertEquals(1, count($qp->get()), 'Check tag.');
+ $this->assertEquals($qp->get(0)->tagName, 'root');
+
+ $string = '<?xml version="1.0"?><root class="findme">Test</root>';
+ $qp = qp($string)->find('.findme');
+ $this->assertEquals(1, count($qp->get()), 'Check class.');
+ $this->assertEquals($qp->get(0)->tagName, 'root');
+ }
/**
* @group basic
@@ -658,8 +676,10 @@ public function testAppend() {
$file = DATA_FILE;
$this->assertEquals(1, qp($file,'unary')->append('<test/>')->find(':root > unary > test')->size());
$qp = qp($file,'#inner-one')->append('<li id="appended"/>');
- $this->assertEquals(1, $qp->find('#appended')->size());
- $this->assertNull($qp->get(0)->nextSibling);
+
+ $appended = $qp->find('#appended');
+ $this->assertEquals(1, $appended->size());
+ $this->assertNull($appended->get(0)->nextSibling);
$this->assertEquals(2, qp($file, 'inner')->append('<test/>')->top()->find('test')->size());
$this->assertEquals(2, qp($file, 'inner')->append(qp('<?xml version="1.0"?><test/>'))->top()->find('test')->size());
@@ -916,8 +936,8 @@ public function testAdd() {
}
public function testEnd() {
- $file = DATA_FILE;
- $this->assertEquals(2, qp($file, 'inner')->find('li')->end()->size());
+ //$file = DATA_FILE;
+ //$this->assertEquals(2, qp($file, 'inner')->find('li')->end()->size());
}
public function testAndSelf() {
@@ -928,6 +948,9 @@ public function testAndSelf() {
public function testChildren() {
$file = DATA_FILE;
$this->assertEquals(5, qp($file, 'inner')->children()->size());
+ foreach (qp($file, 'inner')->children('li') as $kid) {
+ $this->assertEquals('li', $kid->tag());
+ }
$this->assertEquals(5, qp($file, 'inner')->children('li')->size());
$this->assertEquals(1, qp($file, ':root')->children('unary')->size());
}
@@ -952,7 +975,8 @@ public function testContents() {
</div>
</div>
</body></html>';
- $this->assertEquals(7, count($this->contentsRecurse(qp($xml))));
+ $cr = $this->contentsRecurse(qp($xml));
+ $this->assertEquals(7, count($cr), implode("\n", $cr));
}
/**
@@ -960,8 +984,9 @@ public function testContents() {
* Based on problem reported in issue 51.
*/
private function contentsRecurse($source, &$pack = array()) {
+ static $i = 0;
$children = $source->contents();
- $pack[] = $source->html();
+ $pack[] = ++$i . ' "' . $children->html() . '" ';
foreach ($children as $child) {
$pack += $this->contentsRecurse($child, $pack);
@@ -1355,7 +1380,7 @@ public function testNext() {
// Regression test for issue eabrand identified:
- $qp = qp(\QueryPath::HTML_STUB)->append('<div></div><p>Hello</p><p>Goodbye</p>')
+ $qp = qp(\QueryPath::HTML_STUB, 'body')->append('<div></div><p>Hello</p><p>Goodbye</p>')
->children('p')
->after('<p>new paragraph</p>');
@@ -1363,10 +1388,13 @@ public function testNext() {
//throw new Exception($qp->top()->xml());
- $this->assertEquals('Hello', $qp->top('p:first-of-type')->text(), "Test First P ");
+ $qp = $qp->top('p:first-of-type');
+ $this->assertEquals('Hello', $qp->text(), "Test First P " . $qp->top()->html());
$i = 0;
while($qp->next('p')->html() != null) {
- $this->assertEquals($testarray[$i], $qp->text(), $i." didn't match");
+ $qp = $qp->next('p');
+ $this->assertEquals(1, count($qp));
+ $this->assertEquals($testarray[$i], $qp->text(), $i . " didn't match " . $qp->top()->xml() );
$i++;
}
$this->assertEquals(3, $i);
@@ -1390,11 +1418,6 @@ public function testPrevAll() {
$this->assertEquals(3, qp($file, '#four')->prevAll()->size());
$this->assertEquals(2, qp($file, 'foot')->prevAll('inner')->size());
}
- public function testPeers() {
- $file = DATA_FILE;
- $this->assertEquals(3, qp($file, '#two')->peers()->size());
- $this->assertEquals(2, qp($file, 'foot')->peers('inner')->size());
- }
public function testParent() {
$file = DATA_FILE;
$this->assertEquals('root', qp($file, 'unary')->parent()->tag());
@@ -1469,7 +1492,7 @@ public function test__clone() {
$qp = qp($file, 'inner:first-of-type');
$qp2 = clone $qp;
$this->assertFalse($qp === $qp2);
- $qp2->find('li')->attr('foo', 'bar');
+ $qp2->findInPlace('li')->attr('foo', 'bar');
$this->assertEquals('', $qp->find('li')->attr('foo'));
$this->assertEquals('bar', $qp2->attr('foo'), $qp2->top()->xml());
}
@@ -1484,7 +1507,7 @@ public function testIterator() {
$this->assertEquals(4, $qp->find('li')->size());
$i = 0;
- foreach ($qp as $li) {
+ foreach ($qp->find('li') as $li) {
++$i;
$li->text('foo');
}
@@ -1599,7 +1622,7 @@ public function testHas() {
// Test with DOMNode object
$qp = qp($file, 'foot');
$selector = $qp->get(0);
- $qp->top('root')->has($selector);
+ $qp = $qp->top('root')->has($selector);
// This should have one element named 'root'.
$this->assertEquals(1, $qp->size(), 'One element is a parent of foot');
Please sign in to comment.
Something went wrong with that request. Please try again.