[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));
}
$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);
}
@ -799,4 +791,43 @@ class Crawler extends \SplObjectStorage
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()
{
$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());
}
@ -391,6 +391,15 @@ EOF
$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
*/
@ -411,7 +420,7 @@ EOF
{
$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());
}