Skip to content

Commit 497a140

Browse files
committed
minor #2351 [Icons] Prepare lock / warmup optimization (smnandre)
This PR was merged into the 2.x branch. Discussion ---------- [Icons] Prepare lock / warmup optimization Method needed for both * lock/warmup optimization * sprite generation * search performances Commits ------- 218c000 [Icons] Group icons fetching per prefix
2 parents 8215922 + 218c000 commit 497a140

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

src/Icons/src/Iconify.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,54 @@ public function fetchIcon(string $prefix, string $name): Icon
8585
]);
8686
}
8787

88+
public function fetchIcons(string $prefix, array $names): array
89+
{
90+
if (!isset($this->sets()[$prefix])) {
91+
throw new IconNotFoundException(\sprintf('The icon set "%s" does not exist on iconify.design.', $prefix));
92+
}
93+
94+
// Sort to enforce cache hits
95+
sort($names);
96+
$queryString = implode(',', $names);
97+
if (!preg_match('#^[a-z0-9-,]+$#', $queryString)) {
98+
throw new \InvalidArgumentException('Invalid icon names.');
99+
}
100+
101+
// URL must be 500 chars max (iconify limit)
102+
// -39 chars: https://api.iconify.design/XXX.json?icons=
103+
// -safe margin
104+
if (450 < \strlen($prefix.$queryString)) {
105+
throw new \InvalidArgumentException('The query string is too long.');
106+
}
107+
108+
$response = $this->http->request('GET', \sprintf('/%s.json', $prefix), [
109+
'headers' => [
110+
'Accept' => 'application/json',
111+
],
112+
'query' => [
113+
'icons' => strtolower($queryString),
114+
],
115+
]);
116+
117+
if (200 !== $response->getStatusCode()) {
118+
throw new IconNotFoundException(\sprintf('The icon set "%s" does not exist on iconify.design.', $prefix));
119+
}
120+
121+
$data = $response->toArray();
122+
123+
$icons = [];
124+
foreach ($data['icons'] as $iconName => $iconData) {
125+
$height = $iconData['height'] ?? $data['height'] ??= $this->sets()[$prefix]['height'] ?? null;
126+
$width = $iconData['width'] ?? $data['width'] ??= $this->sets()[$prefix]['width'] ?? null;
127+
128+
$icons[$iconName] = new Icon($iconData['body'], [
129+
'viewBox' => \sprintf('0 0 %d %d', $width ?? $height, $height ?? $width),
130+
]);
131+
}
132+
133+
return $icons;
134+
}
135+
88136
public function hasIconSet(string $prefix): bool
89137
{
90138
return isset($this->sets()[$prefix]);

src/Icons/tests/Unit/IconifyTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\HttpClient\MockHttpClient;
1717
use Symfony\Component\HttpClient\Response\JsonMockResponse;
1818
use Symfony\UX\Icons\Exception\IconNotFoundException;
19+
use Symfony\UX\Icons\Icon;
1920
use Symfony\UX\Icons\Iconify;
2021

2122
/**
@@ -162,6 +163,71 @@ public function testFetchIconThrowsWhenStatusCodeNot200(): void
162163
$iconify->fetchIcon('bi', 'heart');
163164
}
164165

166+
public function testFetchIcons(): void
167+
{
168+
$iconify = new Iconify(
169+
cache: new NullAdapter(),
170+
endpoint: 'https://example.com',
171+
http: new MockHttpClient([
172+
new JsonMockResponse([
173+
'bi' => [],
174+
]),
175+
new JsonMockResponse([
176+
'icons' => [
177+
'heart' => [
178+
'body' => '<path d="M0 0h24v24H0z" fill="none"/>',
179+
'height' => 17,
180+
],
181+
'bar' => [
182+
'body' => '<path d="M0 0h24v24H0z" fill="none"/>',
183+
'height' => 17,
184+
],
185+
],
186+
]),
187+
]),
188+
);
189+
190+
$icons = $iconify->fetchIcons('bi', ['heart', 'bar']);
191+
192+
$this->assertCount(2, $icons);
193+
$this->assertSame(['heart', 'bar'], array_keys($icons));
194+
$this->assertContainsOnlyInstancesOf(Icon::class, $icons);
195+
}
196+
197+
public function testFetchIconsThrowsWithInvalidIconNames(): void
198+
{
199+
$iconify = new Iconify(
200+
cache: new NullAdapter(),
201+
endpoint: 'https://example.com',
202+
http: new MockHttpClient([
203+
new JsonMockResponse([
204+
'bi' => [],
205+
]),
206+
]),
207+
);
208+
209+
$this->expectException(\InvalidArgumentException::class);
210+
211+
$iconify->fetchIcons('bi', ['à', 'foo']);
212+
}
213+
214+
public function testFetchIconsThrowsWithTooManyIcons(): void
215+
{
216+
$iconify = new Iconify(
217+
cache: new NullAdapter(),
218+
endpoint: 'https://example.com',
219+
http: new MockHttpClient([
220+
new JsonMockResponse([
221+
'bi' => [],
222+
]),
223+
]),
224+
);
225+
226+
$this->expectException(\InvalidArgumentException::class);
227+
228+
$iconify->fetchIcons('bi', array_fill(0, 50, '1234567890'));
229+
}
230+
165231
public function testGetMetadata(): void
166232
{
167233
$responseFile = __DIR__.'/../Fixtures/Iconify/collections.json';

0 commit comments

Comments
 (0)