Skip to content

Commit

Permalink
[DomCrawler] Added support for an automatic default namespace registr…
Browse files Browse the repository at this point in the history
…ation.
  • Loading branch information
jakzal committed Sep 22, 2013
1 parent 587e2dd commit c6fbb13
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 12 deletions.
51 changes: 41 additions & 10 deletions src/Symfony/Component/DomCrawler/Crawler.php
Expand Up @@ -577,16 +577,8 @@ public function filterXPath($xpath)
$root->appendChild($document->importNode($node, true));
}

$domxpath = new \DOMXPath($document);
if (preg_match_all('/(?P<prefix>[a-zA-Z_][a-zA-Z_0-9\-\.]+):[^:]/', $xpath, $matches)) {
foreach ($matches['prefix'] as $prefix) {
// ask for one namespace, otherwise we'd get a collection with an item for each node
$namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $prefix));
foreach ($namespaces as $node) {
$domxpath->registerNamespace($node->prefix, $node->nodeValue);
}
}
}
$prefixes = $this->findNamespacePrefixes($xpath);
$domxpath = $this->createDOMXPath($document, $prefixes);

return new static($domxpath->query($xpath), $this->uri);
}
Expand Down Expand Up @@ -799,4 +791,43 @@ protected function sibling($node, $siblingDir = 'nextSibling')

return $nodes;
}

/**
* @param \DOMDocument $document
* @param array $prefixes
*
* @return \DOMXPath
*
* @throws \InvalidArgumentException
*/
private function createDOMXPath(\DOMDocument $document, array $prefixes = array())
{
$domxpath = new \DOMXPath($document);

foreach ($prefixes as $prefix) {
// ask for one namespace, otherwise we'd get a collection with an item for each node
$namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', 'default' === $prefix ? '' : $prefix));
if ($node = $namespaces->item(0)) {
$domxpath->registerNamespace($prefix, $node->nodeValue);
} else {
throw new \InvalidArgumentException(sprintf('Could not find a namespace for the prefix: "%s"', $prefix));
}
}

return $domxpath;
}

/**
* @param $xpath
*
* @return array
*/
private function findNamespacePrefixes($xpath)
{
if (preg_match_all('/(?P<prefix>[a-zA-Z_][a-zA-Z_0-9\-\.]+):[^:]/', $xpath, $matches)) {
return array_unique($matches['prefix']);
}

return array();
}
}
13 changes: 11 additions & 2 deletions src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
Expand Up @@ -373,7 +373,7 @@ public function testFilterXPath()

public function testFilterXPathWithDefaultNamespace()
{
$crawler = $this->createTestXmlCrawler()->filterXPath('//entry/id');
$crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id');
$this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace');
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
}
Expand All @@ -391,6 +391,15 @@ public function testFilterXPathWithMultipleNamespaces()
$this->assertSame('widescreen', $crawler->text());
}

/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Could not find a namespace for the prefix: "foo"
*/
public function testFilterXPathWithAnInvalidNamespace()
{
$this->createTestXmlCrawler()->filterXPath('//media:group/foo:aspectRatio');
}

/**
* @covers Symfony\Component\DomCrawler\Crawler::filter
*/
Expand All @@ -411,7 +420,7 @@ public function testFilterWithDefaultNamespace()
{
$this->markSkippedIfCssSelectorNotPresent();

$crawler = $this->createTestXmlCrawler()->filter('entry id');
$crawler = $this->createTestXmlCrawler()->filter('default|entry default|id');
$this->assertCount(1, $crawler, '->filter() automatically registers namespaces');
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
}
Expand Down

0 comments on commit c6fbb13

Please sign in to comment.