diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 316d0083c65a..9a30851ac3c1 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -592,6 +592,36 @@ public function html() return $html; } + /** + * Evaluates an XPath expression. + * + * Since an XPath expression might evaluate to either a simple type or a \DOMDoneList, + * this method will return either an array of simple types or a new Crawler instance. + * + * @param string $xpath An XPath expression + * + * @return array|Crawler An array of evaluation results or a new Crawler instance + */ + public function evaluate($xpath) + { + if (null === $this->document) { + throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.'); + } + + $data = array(); + $domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath)); + + foreach ($this->nodes as $node) { + $data[] = $domxpath->evaluate($xpath, $node); + } + + if (isset($data[0]) && $data[0] instanceof \DOMNodeList) { + return $this->createSubCrawler($data); + } + + return $data; + } + /** * Extracts information from the list of nodes. * diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index b54563b7c64f..85722adc3764 100755 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -1061,6 +1061,51 @@ public function testCountOfNestedElements() $this->assertCount(1, $crawler->filter('li:contains("List item 1")')); } + public function testEvaluateReturnsTypedResultOfXPathExpressionOnADocumentSubset() + { + $crawler = $this->createTestCrawler(); + + $result = $crawler->filterXPath('//form/input')->evaluate('substring-before(@name, "Name")'); + + $this->assertSame(array('Text', 'Foo', 'Bar'), $result); + } + + public function testEvaluateReturnsTypedResultOfNamespacedXPathExpressionOnADocumentSubset() + { + $crawler = $this->createTestXmlCrawler(); + + $result = $crawler->filterXPath('//yt:accessControl/@action')->evaluate('string(.)'); + + $this->assertSame(array('comment', 'videoRespond'), $result); + } + + public function testEvaluateReturnsTypedResultOfNamespacedXPathExpression() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->registerNamespace('youtube', 'http://gdata.youtube.com/schemas/2007'); + + $result = $crawler->evaluate('string(//youtube:accessControl/@action)'); + + $this->assertSame(array('comment'), $result); + } + + public function testEvaluateReturnsACrawlerIfXPathExpressionEvaluatesToANode() + { + $crawler = $this->createTestCrawler()->evaluate('//form/input[1]'); + + $this->assertInstanceOf(Crawler::class, $crawler); + $this->assertCount(1, $crawler); + $this->assertSame('input', $crawler->first()->nodeName()); + } + + /** + * @expectedException \LogicException + */ + public function testEvaluateThrowsAnExceptionIfDocumentIsEmpty() + { + (new Crawler())->evaluate('//form/input[1]'); + } + public function createTestCrawler($uri = null) { $dom = new \DOMDocument();