Skip to content

[DomCrawler] Callable type hint #14293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/DomCrawler/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

3.0.0
-----

* The first argument of `Crawler::each()` and `Crawler::reduce()` are type hinted with `callable` instead of `Closure`.

2.5.0
-----

Expand Down
22 changes: 11 additions & 11 deletions src/Symfony/Component/DomCrawler/Crawler.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,9 @@ public function eq($position)
}

/**
* Calls an anonymous function on each node of the list.
* Execute a callback on each node of the list.
*
* The anonymous function receives the position and the node wrapped
* The callback receives the position and the node wrapped
* in a Crawler instance as arguments.
*
* Example:
Expand All @@ -334,17 +334,17 @@ public function eq($position)
* return $node->text();
* });
*
* @param \Closure $closure An anonymous function
* @param callable $fn A callback
*
* @return array An array of values returned by the anonymous function
* @return array An array of values returned by the callback
*
* @api
*/
public function each(\Closure $closure)
public function each(callable $fn)
{
$data = array();
foreach ($this as $i => $node) {
$data[] = $closure(new static($node, $this->uri, $this->baseHref), $i);
$data[] = $fn(new static($node, $this->uri, $this->baseHref), $i);
}

return $data;
Expand All @@ -364,21 +364,21 @@ public function slice($offset = 0, $length = -1)
}

/**
* Reduces the list of nodes by calling an anonymous function.
* Reduces the list of nodes by calling a callback.
*
* To remove a node from the list, the anonymous function must return false.
* To remove a node from the list, the callback must return false.
*
* @param \Closure $closure An anonymous function
* @param callable $fn A callback
*
* @return Crawler A Crawler instance with the selected nodes.
*
* @api
*/
public function reduce(\Closure $closure)
public function reduce(callable $fn)
{
$nodes = array();
foreach ($this as $i => $node) {
if (false !== $closure(new static($node, $this->uri, $this->baseHref), $i)) {
if (false !== $fn(new static($node, $this->uri, $this->baseHref), $i)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not work for all types of callables. There is still 1 type of callable passing the typehint but failing for the direct call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you tested the 5 examples in your PR description btw ?

$nodes[] = $node;
}
}
Expand Down
49 changes: 40 additions & 9 deletions src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,22 @@ public function testEq()
$this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist');
}

public function testEach()
/**
* @dataProvider testEachProvider
*/
public function testEach($callback)
{
$data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) {
return $i.'-'.$node->text();
});
$data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each($callback);

$this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list');
$this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes a callback on each node of the list');
}

public function testEachProvider()
{
return array(
array(function ($node, $i) { return $i.'-'.$node->text(); }),
array(array(new MyCallback(), 'eachCallback')),
);
}

public function testSlice()
Expand All @@ -323,18 +332,27 @@ public function testSlice()
$this->assertCount(1, $crawler->slice(1, 1), '->slice() slices the nodes in the list');
}

public function testReduce()
/**
* @dataProvider testReduceProvider
*/
public function testReduce($callback)
{
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
$nodes = $crawler->reduce(function ($node, $i) {
return $i !== 1;
});
$nodes = $crawler->reduce($callback);
$this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');

$this->assertCount(2, $nodes, '->reduce() filters the nodes in the list');
}

public function testReduceProvider()
{
return array(
array(function ($node, $i) { return $i !== 1; }),
array(array(new MyCallback(), 'reduceCallback')),
);
}

public function testAttr()
{
$this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list');
Expand Down Expand Up @@ -1070,3 +1088,16 @@ protected function createNodeList()
return $domxpath->query('//div');
}
}

class MyCallback
{
public function eachCallback($node, $i)
{
return $i.'-'.$node->text();
}

public function reduceCallback($node, $i)
{
return $i !== 1;
}
}