minor #24556 [Routing] Ensure uniqueness without repeated check (endroid)

This PR was squashed before being merged into the 3.4 branch (closes #24556).

Discussion
----------

[Routing] Ensure uniqueness without repeated check

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

The RouteCollection ensures uniqueness of resources by using array_unique. The current implementation suffers from the following problems.

1. The array_unique method calls __toString for every element in the array but it is very inefficient in terms of the number of calls made. I.e. if the array has 50 elements it does not do 50 __toString calls. I did some tests with Blackfire in PHP 7.1 and found the following numbers.

25 elements => 240 calls
50 elements => 607 calls
100 elements => 1382 calls
200 elements => 3333 calls

2. The array_unique function is called every time the getResources() method is called, even when the resources did not change in the mean time. Combined with the above this leads to lower performance.

Many applications have a low number of routes so this is not a big issue. But for larger applications or bundles that generate routes (i.e. for CRUD or API / doc generation) this will have a bigger impact.

Commits
-------

16f7281178 [Routing] Ensure uniqueness without repeated check
This commit is contained in:
Fabien Potencier 2017-10-16 13:33:36 -07:00
commit f757cd65e2

View File

@ -128,7 +128,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
$this->routes[$name] = $route;
}
$this->resources = array_merge($this->resources, $collection->getResources());
foreach ($collection->getResources() as $resource) {
$this->addResource($resource);
}
}
/**
@ -262,16 +264,21 @@ class RouteCollection implements \IteratorAggregate, \Countable
*/
public function getResources()
{
return array_unique($this->resources);
return array_values($this->resources);
}
/**
* Adds a resource for this collection.
* Adds a resource for this collection. If the resource already exists
* it is not added.
*
* @param ResourceInterface $resource A resource instance
*/
public function addResource(ResourceInterface $resource)
{
$this->resources[] = $resource;
$key = (string) $resource;
if (!isset($this->resources[$key])) {
$this->resources[$key] = $resource;
}
}
}