[DomCrawler] Added support for an automatic default namespace registration.

This commit is contained in:
Jakub Zalas 2013-09-17 18:15:30 +01:00
parent 587e2dd44f
commit c6fbb13938
2 changed files with 52 additions and 12 deletions

View File

@ -577,16 +577,8 @@ class Crawler extends \SplObjectStorage
$root->appendChild($document->importNode($node, true)); $root->appendChild($document->importNode($node, true));
} }
$domxpath = new \DOMXPath($document); $prefixes = $this->findNamespacePrefixes($xpath);
if (preg_match_all('/(?P<prefix>[a-zA-Z_][a-zA-Z_0-9\-\.]+):[^:]/', $xpath, $matches)) { $domxpath = $this->createDOMXPath($document, $prefixes);
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);
}
}
}
return new static($domxpath->query($xpath), $this->uri); return new static($domxpath->query($xpath), $this->uri);
} }
@ -799,4 +791,43 @@ class Crawler extends \SplObjectStorage
return $nodes; 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();
}
} }

View File

@ -373,7 +373,7 @@ EOF
public function testFilterXPathWithDefaultNamespace() 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->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace');
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
} }
@ -391,6 +391,15 @@ EOF
$this->assertSame('widescreen', $crawler->text()); $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 * @covers Symfony\Component\DomCrawler\Crawler::filter
*/ */
@ -411,7 +420,7 @@ EOF
{ {
$this->markSkippedIfCssSelectorNotPresent(); $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->assertCount(1, $crawler, '->filter() automatically registers namespaces');
$this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
} }