├── .gitignore
├── .htaccess
├── cache
├── .htaccess
└── index.html
├── composer.json
├── config-default.php
├── config.md
├── git-repos
├── .htaccess
└── index.php
├── index.php
├── json
└── arty-animated.json
├── lib
├── Dirs.php
├── Query.php
├── Registry.php
├── Repositories.php
└── Sync.php
├── package.json
├── phpunit.xml.dist
├── readme.md
├── robots.txt
└── tests
└── query_test.php
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | .idea
3 | .DS_Store
4 | config.json
5 | config.php
6 | cache/*.php
7 | vendor
8 | composer.phar
9 | composer.lock
10 | /git-repos/*
11 | !/git-repos/.htaccess
12 | !/git-repos/index.php
13 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine on
2 | RewriteCond %{REQUEST_FILENAME} !-d
3 | RewriteCond %{REQUEST_FILENAME} !-f
4 | RewriteRule . index.php [L]
--------------------------------------------------------------------------------
/cache/.htaccess:
--------------------------------------------------------------------------------
1 | Order deny,allow
2 | Deny from all
--------------------------------------------------------------------------------
/cache/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "PHP version of api.iconify.design",
3 | "version": "1.0.0",
4 | "type": "project",
5 | "license": "MIT",
6 | "homepage": "https://iconify.design/",
7 | "require": {
8 | "php": ">=5.6",
9 | "iconify/json": "*",
10 | "iconify/json-tools": "*"
11 | },
12 | "require-dev": {
13 | "phpunit/phpunit": "*"
14 | },
15 | "config": {
16 | "bin-dir": "bin"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "Iconify\\API\\": "lib"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/config-default.php:
--------------------------------------------------------------------------------
1 | '',
5 | 'env-region' => true,
6 | 'custom-icons-dir' => __DIR__ . '/json',
7 | 'serve-default-icons' => true,
8 | 'index-page' => 'https://iconify.design/',
9 | 'cache' => [
10 | 'timeout' => 604800,
11 | 'min-refresh' => 604800,
12 | 'private' => false
13 | ],
14 | 'cors' => [
15 | 'origins' => '*',
16 | 'timeout' => 86400,
17 | 'methods' => 'GET, OPTIONS',
18 | 'headers' => 'Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding',
19 | ],
20 | 'sync' => [
21 | 'versions' => __DIR__ . '/git-repos/versions.json',
22 | 'storage' => __DIR__ . '/git-repos',
23 | 'git' => 'git clone {repo} --depth 1 --no-tags {target}',
24 | 'secret' => '',
25 | 'iconify' => 'https://github.com/iconify/collections-json.git',
26 | 'custom' => '',
27 | 'custom-dir' => ''
28 | ],
29 | 'cache-dir' => __DIR__ . '/cache'
30 | ];
31 |
--------------------------------------------------------------------------------
/config.md:
--------------------------------------------------------------------------------
1 | # Configuration options
2 |
3 | Default options are in config-default.php
4 |
5 | Do not edit config-default.php unless you are making your own fork of project. All custom config options should be added to config.php. Create empty config.php:
6 |
7 | ```
8 | locateCollections();
106 | });
107 |
108 | // Find collection
109 | $collection = $registry->getCollection($prefix);
110 | if ($collection === null) {
111 | sendError(404);
112 | exit(0);
113 | }
114 |
115 | // Parse query
116 | $result = \Iconify\API\Query::parse($collection, $query, $ext, $_GET);
117 |
118 | if (is_numeric($result)) {
119 | sendError($result);
120 | exit(0);
121 | }
122 |
123 | // Get collection cache time
124 | $time = $registry->getCollectionTime($prefix);
125 | if ($time) {
126 | header('Last-Modified: ' . gmdate('D, d M Y H:i:s T', $time));
127 | }
128 |
129 | // Send response
130 | sendCacheHeaders($config);
131 |
132 | header('Content-Type: ' . $result['type']);
133 | header('ETag: ' . md5($result['body']));
134 |
135 | // Check for download
136 | if (isset($result['filename']) && isset($_GET['download']) && ($_GET['download'] === '1' || $_GET['download'] === 'true')) {
137 | header('Content-Disposition: attachment; filename="' . $result['filename'] . '"');
138 | }
139 |
140 | // Echo body and exit
141 | echo $result['body'];
142 | exit(0);
143 | }
144 |
145 | // Load config
146 | $config = getCustomConfig($config);
147 |
148 | // Check Origin
149 | if (isset($_SERVER['HTTP_ORIGIN']) && $config['cors']) {
150 | header('Access-Control-Allow-Origin: ' . $config['cors']['origins']);
151 | header('Access-Control-Allow-Credentials: true');
152 | header('Access-Control-Max-Age: ' . $config['cors']['timeout']);
153 | }
154 |
155 | // Access-Control headers are received during OPTIONS requests
156 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
157 | if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
158 | header('Access-Control-Allow-Methods: ' . $config['cors']['methods']);
159 | }
160 |
161 | if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
162 | header('Access-Control-Allow-Headers: ' . $config['cors']['headers']);
163 | }
164 |
165 | cacheHeaders($config);
166 | exit(0);
167 | }
168 |
169 | // Check for cache header
170 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
171 | $time = @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
172 | if ($config['cache'] && (!$time || $time > time() - $config['cache']['timeout'])) {
173 | http_response_code(304);
174 | exit(0);
175 | }
176 | }
177 |
178 | // Get URL
179 | $url = $_SERVER['REQUEST_URI'];
180 | $script = $_SERVER['SCRIPT_NAME'];
181 | $prefix = substr($script, 0, strlen($script) - strlen('index.php'));
182 | $url = substr($url, strlen($prefix));
183 | $url = explode('?', $url);
184 | $url = $url[0];
185 |
186 | if ($url === '') {
187 | // Index
188 | http_response_code(301);
189 | header('Location: ' . $config['index-page']);
190 | exit(0);
191 | }
192 |
193 | if ($url === 'version') {
194 | // Send version response
195 | $package = file_get_contents(__DIR__ . '/composer.json');
196 | $data = json_decode($package, true);
197 | $version = $data['version'];
198 | echo 'Iconify API version ', $version, ' (PHP';
199 |
200 | // Try to get region
201 | if ($config['env-region']) {
202 | $value = getenv('region');
203 | if ($value !== false) {
204 | $config['region'] = $value;
205 | }
206 | }
207 | if ($config['region'] !== '') {
208 | echo ', ', $config['region'];
209 | }
210 |
211 | echo ')';
212 | exit(0);
213 | }
214 |
215 | if ($url === 'sync') {
216 | // Synchronize repository
217 | if (!isset($_REQUEST['repo']) || !isset($_REQUEST['key']) || !is_string($_REQUEST['repo']) || !is_string($_REQUEST['key'])) {
218 | sendError(400);
219 | exit(0);
220 | }
221 |
222 | $start = time();
223 | if (!isset($config['sync']) || empty($config['sync']['secret']) || $_REQUEST['key'] !== $config['sync']['secret']) {
224 | $result = false;
225 | } else {
226 | $sync = new \Iconify\API\Sync($config);
227 | $result = $sync->sync($_REQUEST['repo']);
228 | }
229 |
230 | // PHP cannot send response and then do stuff, so fake doing stuff few seconds
231 | $limit = 15;
232 | $end = time();
233 | $diff = $end - $start;
234 | if ($diff < $limit) {
235 | sleep($limit - $diff);
236 | }
237 |
238 | exit(0);
239 | }
240 |
241 | // Split URL parts
242 | $url_parts = explode('.', $url);
243 | if (count($url_parts) !== 2) {
244 | sendError(404);
245 | exit(0);
246 | }
247 | $ext = $url_parts[1];
248 | if (!preg_match('/^[a-z0-9:\/-]+$/', $url_parts[0])) {
249 | sendError(404);
250 | exit(0);
251 | }
252 | $url_parts = explode('/', $url_parts[0]);
253 |
254 | // Send to correct handler
255 | switch (count($url_parts)) {
256 | case 1:
257 | // 1 part request
258 | if ($ext === 'svg') {
259 | $parts = explode(':', $url_parts[0]);
260 | if (count($parts) === 2) {
261 | // prefix:icon.svg
262 | parseRequest($parts[0], $parts[1], $ext);
263 | } elseif (count($parts) === 1) {
264 | $parts = explode('-', $parts[0]);
265 | if (count($parts) > 1) {
266 | // prefix-icon.svg
267 | parseRequest(array_shift($parts), implode('-', $parts), $ext);
268 | }
269 | }
270 | } elseif ($ext === 'js' || $ext === 'json') {
271 | // prefix.json
272 | parseRequest($url_parts[0], 'icons', $ext);
273 | }
274 | break;
275 |
276 | case 2:
277 | // 2 part request
278 | if ($ext === 'js' || $ext === 'json' || $ext === 'svg') {
279 | // prefix/icon.svg
280 | // prefix/icons.json
281 | parseRequest($url_parts[0], $url_parts[1], $ext);
282 | }
283 | break;
284 | }
285 |
286 | // Invalid request
287 | sendError(404);
288 | exit(0);
289 |
--------------------------------------------------------------------------------
/json/arty-animated.json:
--------------------------------------------------------------------------------
1 | {
2 | "prefix": "arty-animated",
3 | "icons": {
4 | "16-arc-180": {
5 | "body": ""
6 | },
7 | "16-arc-270": {
8 | "body": ""
9 | },
10 | "16-arc-90": {
11 | "body": ""
12 | },
13 | "16-arrow-left": {
14 | "body": ""
15 | },
16 | "16-arrows-from-2-corners": {
17 | "body": ""
18 | },
19 | "16-arrows-from-corners": {
20 | "body": ""
21 | },
22 | "16-arrows-horizontal": {
23 | "body": ""
24 | },
25 | "16-arrows-to-2-corners": {
26 | "body": ""
27 | },
28 | "16-arrows-to-corners": {
29 | "body": ""
30 | },
31 | "16-caret-up-outline": {
32 | "body": ""
33 | },
34 | "16-caret-up": {
35 | "body": ""
36 | },
37 | "16-carets-vertical-outline": {
38 | "body": ""
39 | },
40 | "16-carets-vertical": {
41 | "body": ""
42 | },
43 | "16-chevron-left": {
44 | "body": ""
45 | },
46 | "16-close": {
47 | "body": ""
48 | },
49 | "16-confirm": {
50 | "body": ""
51 | },
52 | "16-double-arrow-horizontal": {
53 | "body": ""
54 | },
55 | "16-double-small-chevron-left": {
56 | "body": ""
57 | },
58 | "16-drop-outline": {
59 | "body": ""
60 | },
61 | "16-drop": {
62 | "body": ""
63 | },
64 | "16-filters": {
65 | "body": ""
66 | },
67 | "16-grid-3-outline": {
68 | "body": ""
69 | },
70 | "16-grid-3": {
71 | "body": ""
72 | },
73 | "16-home": {
74 | "body": ""
75 | },
76 | "16-list-3-outline": {
77 | "body": ""
78 | },
79 | "16-list-3": {
80 | "body": ""
81 | },
82 | "16-panel-left": {
83 | "body": ""
84 | },
85 | "16-search": {
86 | "body": ""
87 | },
88 | "16-small-chevron-left": {
89 | "body": ""
90 | },
91 | "20-arc-180": {
92 | "body": "",
93 | "width": 160,
94 | "height": 160
95 | },
96 | "20-arc-270": {
97 | "body": "",
98 | "width": 160,
99 | "height": 160
100 | },
101 | "20-arc-90": {
102 | "body": "",
103 | "width": 160,
104 | "height": 160
105 | },
106 | "20-arrow-left": {
107 | "body": "",
108 | "width": 160,
109 | "height": 160
110 | },
111 | "20-arrows-from-2-corners": {
112 | "body": "",
113 | "width": 160,
114 | "height": 160
115 | },
116 | "20-arrows-from-corners": {
117 | "body": "",
118 | "width": 160,
119 | "height": 160
120 | },
121 | "20-arrows-horizontal": {
122 | "body": "",
123 | "width": 160,
124 | "height": 160
125 | },
126 | "20-arrows-to-2-corners": {
127 | "body": "",
128 | "width": 160,
129 | "height": 160
130 | },
131 | "20-arrows-to-corners": {
132 | "body": "",
133 | "width": 160,
134 | "height": 160
135 | },
136 | "20-caret-up-outline": {
137 | "body": "",
138 | "width": 160,
139 | "height": 160
140 | },
141 | "20-caret-up": {
142 | "body": "",
143 | "width": 160,
144 | "height": 160
145 | },
146 | "20-carets-vertical-outline": {
147 | "body": "",
148 | "width": 160,
149 | "height": 160
150 | },
151 | "20-carets-vertical": {
152 | "body": "",
153 | "width": 160,
154 | "height": 160
155 | },
156 | "20-chevron-left": {
157 | "body": "",
158 | "width": 160,
159 | "height": 160
160 | },
161 | "20-close": {
162 | "body": "",
163 | "width": 160,
164 | "height": 160
165 | },
166 | "20-confirm": {
167 | "body": "",
168 | "width": 160,
169 | "height": 160
170 | },
171 | "20-double-arrow-horizontal": {
172 | "body": "",
173 | "width": 160,
174 | "height": 160
175 | },
176 | "20-double-small-chevron-left": {
177 | "body": "",
178 | "width": 160,
179 | "height": 160
180 | },
181 | "20-drop-outline": {
182 | "body": "",
183 | "width": 160,
184 | "height": 160
185 | },
186 | "20-drop": {
187 | "body": "",
188 | "width": 160,
189 | "height": 160
190 | },
191 | "20-filters": {
192 | "body": "",
193 | "width": 160,
194 | "height": 160
195 | },
196 | "20-grid-3-outline": {
197 | "body": "",
198 | "width": 160,
199 | "height": 160
200 | },
201 | "20-grid-3": {
202 | "body": "",
203 | "width": 160,
204 | "height": 160
205 | },
206 | "20-home": {
207 | "body": "",
208 | "width": 160,
209 | "height": 160
210 | },
211 | "20-list-3-outline": {
212 | "body": "",
213 | "width": 160,
214 | "height": 160
215 | },
216 | "20-list-3": {
217 | "body": "",
218 | "width": 160,
219 | "height": 160
220 | },
221 | "20-panel-left": {
222 | "body": "",
223 | "width": 160,
224 | "height": 160
225 | },
226 | "20-search": {
227 | "body": "",
228 | "width": 160,
229 | "height": 160
230 | },
231 | "20-small-chevron-left": {
232 | "body": "",
233 | "width": 160,
234 | "height": 160
235 | },
236 | "24-arc-180": {
237 | "body": "",
238 | "width": 192,
239 | "height": 192
240 | },
241 | "24-arc-270": {
242 | "body": "",
243 | "width": 192,
244 | "height": 192
245 | },
246 | "24-arc-90": {
247 | "body": "",
248 | "width": 192,
249 | "height": 192
250 | },
251 | "24-arrow-left": {
252 | "body": "",
253 | "width": 192,
254 | "height": 192
255 | },
256 | "24-arrows-from-2-corners": {
257 | "body": "",
258 | "width": 192,
259 | "height": 192
260 | },
261 | "24-arrows-from-corners": {
262 | "body": "",
263 | "width": 192,
264 | "height": 192
265 | },
266 | "24-arrows-horizontal": {
267 | "body": "",
268 | "width": 192,
269 | "height": 192
270 | },
271 | "24-arrows-to-2-corners": {
272 | "body": "",
273 | "width": 192,
274 | "height": 192
275 | },
276 | "24-arrows-to-corners": {
277 | "body": "",
278 | "width": 192,
279 | "height": 192
280 | },
281 | "24-caret-up-outline": {
282 | "body": "",
283 | "width": 192,
284 | "height": 192
285 | },
286 | "24-caret-up": {
287 | "body": "",
288 | "width": 192,
289 | "height": 192
290 | },
291 | "24-carets-vertical-outline": {
292 | "body": "",
293 | "width": 192,
294 | "height": 192
295 | },
296 | "24-carets-vertical": {
297 | "body": "",
298 | "width": 192,
299 | "height": 192
300 | },
301 | "24-chevron-left": {
302 | "body": "",
303 | "width": 192,
304 | "height": 192
305 | },
306 | "24-close": {
307 | "body": "",
308 | "width": 192,
309 | "height": 192
310 | },
311 | "24-confirm": {
312 | "body": "",
313 | "width": 192,
314 | "height": 192
315 | },
316 | "24-double-arrow-horizontal": {
317 | "body": "",
318 | "width": 192,
319 | "height": 192
320 | },
321 | "24-double-small-chevron-left": {
322 | "body": "",
323 | "width": 192,
324 | "height": 192
325 | },
326 | "24-drop-outline": {
327 | "body": "",
328 | "width": 192,
329 | "height": 192
330 | },
331 | "24-drop": {
332 | "body": "",
333 | "width": 192,
334 | "height": 192
335 | },
336 | "24-filters": {
337 | "body": "",
338 | "width": 192,
339 | "height": 192
340 | },
341 | "24-grid-3-outline": {
342 | "body": "",
343 | "width": 192,
344 | "height": 192
345 | },
346 | "24-grid-3": {
347 | "body": "",
348 | "width": 192,
349 | "height": 192
350 | },
351 | "24-home": {
352 | "body": "",
353 | "width": 192,
354 | "height": 192
355 | },
356 | "24-list-3-outline": {
357 | "body": "",
358 | "width": 192,
359 | "height": 192
360 | },
361 | "24-list-3": {
362 | "body": "",
363 | "width": 192,
364 | "height": 192
365 | },
366 | "24-panel-left": {
367 | "body": "",
368 | "width": 192,
369 | "height": 192
370 | },
371 | "24-search": {
372 | "body": "",
373 | "width": 192,
374 | "height": 192
375 | },
376 | "24-small-chevron-left": {
377 | "body": "",
378 | "width": 192,
379 | "height": 192
380 | }
381 | },
382 | "aliases": {
383 | "16-arrow-right": {
384 | "parent": "16-arrow-left",
385 | "hFlip": true
386 | },
387 | "16-arrow-up": {
388 | "parent": "16-arrow-left",
389 | "rotate": 1,
390 | "vFlip": true
391 | },
392 | "16-arrow-down": {
393 | "parent": "16-arrow-left",
394 | "rotate": 3
395 | },
396 | "16-arrows-from-2-corners-rotated": {
397 | "parent": "16-arrows-from-2-corners",
398 | "hFlip": true
399 | },
400 | "16-arrows-vertical": {
401 | "parent": "16-arrows-horizontal",
402 | "rotate": 1
403 | },
404 | "16-arrows-to-2-corners-rotated": {
405 | "parent": "16-arrows-to-2-corners",
406 | "hFlip": true
407 | },
408 | "16-caret-down-outline": {
409 | "parent": "16-caret-up-outline",
410 | "vFlip": true
411 | },
412 | "16-caret-left-outline": {
413 | "parent": "16-caret-up-outline",
414 | "rotate": 1,
415 | "vFlip": true
416 | },
417 | "16-caret-right-outline": {
418 | "parent": "16-caret-up-outline",
419 | "rotate": 1
420 | },
421 | "16-caret-down": {
422 | "parent": "16-caret-up",
423 | "vFlip": true
424 | },
425 | "16-caret-left": {
426 | "parent": "16-caret-up",
427 | "rotate": 1,
428 | "vFlip": true
429 | },
430 | "16-caret-right": {
431 | "parent": "16-caret-up",
432 | "rotate": 1
433 | },
434 | "16-carets-horizontal-outline": {
435 | "parent": "16-carets-vertical-outline",
436 | "rotate": 3
437 | },
438 | "16-carets-horizontal": {
439 | "parent": "16-carets-vertical",
440 | "rotate": 3
441 | },
442 | "16-chevron-right": {
443 | "parent": "16-chevron-left",
444 | "hFlip": true
445 | },
446 | "16-chevron-up": {
447 | "parent": "16-chevron-left",
448 | "rotate": 1,
449 | "vFlip": true
450 | },
451 | "16-chevron-down": {
452 | "parent": "16-chevron-left",
453 | "rotate": 3
454 | },
455 | "16-double-arrow-vertical": {
456 | "parent": "16-double-arrow-horizontal",
457 | "rotate": 1
458 | },
459 | "16-double-small-chevron-right": {
460 | "parent": "16-double-small-chevron-left",
461 | "hFlip": true
462 | },
463 | "16-double-small-chevron-up": {
464 | "parent": "16-double-small-chevron-left",
465 | "rotate": 1,
466 | "vFlip": true
467 | },
468 | "16-double-small-chevron-down": {
469 | "parent": "16-double-small-chevron-left",
470 | "rotate": 3
471 | },
472 | "16-filters-horizontal": {
473 | "parent": "16-filters",
474 | "rotate": 3,
475 | "hFlip": true
476 | },
477 | "16-list-3-outline-rtl": {
478 | "parent": "16-list-3-outline",
479 | "hFlip": true
480 | },
481 | "16-list-3-rtl": {
482 | "parent": "16-list-3",
483 | "hFlip": true
484 | },
485 | "16-panel-right": {
486 | "parent": "16-panel-left",
487 | "hFlip": true
488 | },
489 | "16-panel-up": {
490 | "parent": "16-panel-left",
491 | "rotate": 1,
492 | "vFlip": true
493 | },
494 | "16-panel-down": {
495 | "parent": "16-panel-left",
496 | "rotate": 3
497 | },
498 | "16-search-rotated": {
499 | "parent": "16-search",
500 | "rotate": 3
501 | },
502 | "16-small-chevron-right": {
503 | "parent": "16-small-chevron-left",
504 | "hFlip": true
505 | },
506 | "16-small-chevron-up": {
507 | "parent": "16-small-chevron-left",
508 | "rotate": 1,
509 | "vFlip": true
510 | },
511 | "16-small-chevron-down": {
512 | "parent": "16-small-chevron-left",
513 | "rotate": 3
514 | },
515 | "20-arrow-right": {
516 | "parent": "20-arrow-left",
517 | "hFlip": true
518 | },
519 | "20-arrow-up": {
520 | "parent": "20-arrow-left",
521 | "rotate": 1,
522 | "vFlip": true
523 | },
524 | "20-arrow-down": {
525 | "parent": "20-arrow-left",
526 | "rotate": 3
527 | },
528 | "20-arrows-from-2-corners-rotated": {
529 | "parent": "20-arrows-from-2-corners",
530 | "hFlip": true
531 | },
532 | "20-arrows-vertical": {
533 | "parent": "20-arrows-horizontal",
534 | "rotate": 1
535 | },
536 | "20-arrows-to-2-corners-rotated": {
537 | "parent": "20-arrows-to-2-corners",
538 | "hFlip": true
539 | },
540 | "20-caret-down-outline": {
541 | "parent": "20-caret-up-outline",
542 | "vFlip": true
543 | },
544 | "20-caret-left-outline": {
545 | "parent": "20-caret-up-outline",
546 | "rotate": 1,
547 | "vFlip": true
548 | },
549 | "20-caret-right-outline": {
550 | "parent": "20-caret-up-outline",
551 | "rotate": 1
552 | },
553 | "20-caret-down": {
554 | "parent": "20-caret-up",
555 | "vFlip": true
556 | },
557 | "20-caret-left": {
558 | "parent": "20-caret-up",
559 | "rotate": 1,
560 | "vFlip": true
561 | },
562 | "20-caret-right": {
563 | "parent": "20-caret-up",
564 | "rotate": 1
565 | },
566 | "20-carets-horizontal-outline": {
567 | "parent": "20-carets-vertical-outline",
568 | "rotate": 3
569 | },
570 | "20-carets-horizontal": {
571 | "parent": "20-carets-vertical",
572 | "rotate": 3
573 | },
574 | "20-chevron-right": {
575 | "parent": "20-chevron-left",
576 | "hFlip": true
577 | },
578 | "20-chevron-up": {
579 | "parent": "20-chevron-left",
580 | "rotate": 1,
581 | "vFlip": true
582 | },
583 | "20-chevron-down": {
584 | "parent": "20-chevron-left",
585 | "rotate": 3
586 | },
587 | "20-double-arrow-vertical": {
588 | "parent": "20-double-arrow-horizontal",
589 | "rotate": 1
590 | },
591 | "20-double-small-chevron-right": {
592 | "parent": "20-double-small-chevron-left",
593 | "hFlip": true
594 | },
595 | "20-double-small-chevron-up": {
596 | "parent": "20-double-small-chevron-left",
597 | "rotate": 1,
598 | "vFlip": true
599 | },
600 | "20-double-small-chevron-down": {
601 | "parent": "20-double-small-chevron-left",
602 | "rotate": 3
603 | },
604 | "20-filters-horizontal": {
605 | "parent": "20-filters",
606 | "rotate": 3,
607 | "hFlip": true
608 | },
609 | "20-list-3-outline-rtl": {
610 | "parent": "20-list-3-outline",
611 | "hFlip": true
612 | },
613 | "20-list-3-rtl": {
614 | "parent": "20-list-3",
615 | "hFlip": true
616 | },
617 | "20-panel-right": {
618 | "parent": "20-panel-left",
619 | "hFlip": true
620 | },
621 | "20-panel-up": {
622 | "parent": "20-panel-left",
623 | "rotate": 1,
624 | "vFlip": true
625 | },
626 | "20-panel-down": {
627 | "parent": "20-panel-left",
628 | "rotate": 3
629 | },
630 | "20-search-rotated": {
631 | "parent": "20-search",
632 | "rotate": 3
633 | },
634 | "20-small-chevron-right": {
635 | "parent": "20-small-chevron-left",
636 | "hFlip": true
637 | },
638 | "20-small-chevron-up": {
639 | "parent": "20-small-chevron-left",
640 | "rotate": 1,
641 | "vFlip": true
642 | },
643 | "20-small-chevron-down": {
644 | "parent": "20-small-chevron-left",
645 | "rotate": 3
646 | },
647 | "24-arrow-right": {
648 | "parent": "24-arrow-left",
649 | "hFlip": true
650 | },
651 | "24-arrow-up": {
652 | "parent": "24-arrow-left",
653 | "rotate": 1,
654 | "vFlip": true
655 | },
656 | "24-arrow-down": {
657 | "parent": "24-arrow-left",
658 | "rotate": 3
659 | },
660 | "24-arrows-from-2-corners-rotated": {
661 | "parent": "24-arrows-from-2-corners",
662 | "hFlip": true
663 | },
664 | "24-arrows-vertical": {
665 | "parent": "24-arrows-horizontal",
666 | "rotate": 1
667 | },
668 | "24-arrows-to-2-corners-rotated": {
669 | "parent": "24-arrows-to-2-corners",
670 | "hFlip": true
671 | },
672 | "24-caret-down-outline": {
673 | "parent": "24-caret-up-outline",
674 | "vFlip": true
675 | },
676 | "24-caret-left-outline": {
677 | "parent": "24-caret-up-outline",
678 | "rotate": 1,
679 | "vFlip": true
680 | },
681 | "24-caret-right-outline": {
682 | "parent": "24-caret-up-outline",
683 | "rotate": 1
684 | },
685 | "24-caret-down": {
686 | "parent": "24-caret-up",
687 | "vFlip": true
688 | },
689 | "24-caret-left": {
690 | "parent": "24-caret-up",
691 | "rotate": 1,
692 | "vFlip": true
693 | },
694 | "24-caret-right": {
695 | "parent": "24-caret-up",
696 | "rotate": 1
697 | },
698 | "24-carets-horizontal-outline": {
699 | "parent": "24-carets-vertical-outline",
700 | "rotate": 3
701 | },
702 | "24-carets-horizontal": {
703 | "parent": "24-carets-vertical",
704 | "rotate": 3
705 | },
706 | "24-chevron-right": {
707 | "parent": "24-chevron-left",
708 | "hFlip": true
709 | },
710 | "24-chevron-up": {
711 | "parent": "24-chevron-left",
712 | "rotate": 1,
713 | "vFlip": true
714 | },
715 | "24-chevron-down": {
716 | "parent": "24-chevron-left",
717 | "rotate": 3
718 | },
719 | "24-double-arrow-vertical": {
720 | "parent": "24-double-arrow-horizontal",
721 | "rotate": 1
722 | },
723 | "24-double-small-chevron-right": {
724 | "parent": "24-double-small-chevron-left",
725 | "hFlip": true
726 | },
727 | "24-double-small-chevron-up": {
728 | "parent": "24-double-small-chevron-left",
729 | "rotate": 1,
730 | "vFlip": true
731 | },
732 | "24-double-small-chevron-down": {
733 | "parent": "24-double-small-chevron-left",
734 | "rotate": 3
735 | },
736 | "24-filters-horizontal": {
737 | "parent": "24-filters",
738 | "rotate": 3,
739 | "hFlip": true
740 | },
741 | "24-list-3-outline-rtl": {
742 | "parent": "24-list-3-outline",
743 | "hFlip": true
744 | },
745 | "24-list-3-rtl": {
746 | "parent": "24-list-3",
747 | "hFlip": true
748 | },
749 | "24-panel-right": {
750 | "parent": "24-panel-left",
751 | "hFlip": true
752 | },
753 | "24-panel-up": {
754 | "parent": "24-panel-left",
755 | "rotate": 1,
756 | "vFlip": true
757 | },
758 | "24-panel-down": {
759 | "parent": "24-panel-left",
760 | "rotate": 3
761 | },
762 | "24-search-rotated": {
763 | "parent": "24-search",
764 | "rotate": 3
765 | },
766 | "24-small-chevron-right": {
767 | "parent": "24-small-chevron-left",
768 | "hFlip": true
769 | },
770 | "24-small-chevron-up": {
771 | "parent": "24-small-chevron-left",
772 | "rotate": 1,
773 | "vFlip": true
774 | },
775 | "24-small-chevron-down": {
776 | "parent": "24-small-chevron-left",
777 | "rotate": 3
778 | }
779 | },
780 | "width": 128,
781 | "height": 128
782 | }
--------------------------------------------------------------------------------
/lib/Dirs.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the license.txt
9 | * file that was distributed with this source code.
10 | * @license MIT
11 | */
12 |
13 | namespace Iconify\API;
14 |
15 | use \Iconify\IconsJSON\Finder;
16 |
17 | class Dirs {
18 | protected static $_instance = null;
19 |
20 | protected $_config;
21 |
22 | protected $_repos;
23 |
24 | protected $_dirs = [];
25 | protected $_versions = null;
26 | protected $_reposBaseDir = null;
27 |
28 | /**
29 | * Get instance. This class can have only 1 instance
30 | *
31 | * @param $config
32 | * @return Dirs
33 | */
34 | public static function instance($config)
35 | {
36 | if (self::$_instance === null) {
37 | self::$_instance = new Dirs($config);
38 | }
39 | return self::$_instance;
40 | }
41 |
42 | /**
43 | * Constructor
44 | *
45 | * @param array $config
46 | */
47 | protected function __construct($config)
48 | {
49 | $this->_config = $config;
50 | $this->_repos = [];
51 |
52 | // Setup default directories
53 | if (!empty($config['serve-default-icons'])) {
54 | $this->_dirs['iconify'] = Finder::rootDir();
55 | }
56 |
57 | if (!empty($config['custom-icons-dir'])) {
58 | $this->_dirs['custom'] = $config['custom-icons-dir'];
59 | }
60 |
61 | $this->_repos = array_keys($this->_dirs);
62 | $this->_checkSynchronizedRepositories();
63 | }
64 |
65 | /**
66 | * Get repositories time
67 | *
68 | * @return array|null
69 | */
70 | protected function _checkSynchronizedRepositories()
71 | {
72 | if ($this->_versions !== null) {
73 | return $this->_versions;
74 | }
75 |
76 | $this->_versions = [];
77 | if (empty($this->_config['sync']) || empty($this->_config['sync']['secret']) || empty($this->_config['sync']['versions']) || empty($this->_config['sync']['storage'])) {
78 | // Synchronization is inactive
79 | return $this->_versions;
80 | }
81 |
82 | // Check for possible repositories
83 | foreach($this->_repos as $repo) {
84 | if (!empty($this->_config['sync'][$repo])) {
85 | $this->_versions[$repo] = 0;
86 | }
87 | }
88 | if (!count($this->_versions)) {
89 | return $this->_versions;
90 | }
91 |
92 | // Check for versions.json
93 | $data = @file_get_contents($this->_config['sync']['versions']);
94 | $data = @json_decode($data, true);
95 | if (!is_array($data)) {
96 | return $this->_versions;
97 | }
98 |
99 | if ($this->_reposBaseDir === null) {
100 | $this->_getBaseReposDir();
101 | }
102 | foreach ($data as $repo => $value) {
103 | if (!isset($this->_versions[$repo])) {
104 | continue;
105 | }
106 | $dir = $this->_reposBaseDir . '/' . $repo . '.' . $value;
107 | if (@is_dir($dir)) {
108 | $this->setRepositoryDir($repo, $value, $dir);
109 | }
110 | }
111 |
112 | return $this->_versions;
113 | }
114 |
115 | /**
116 | * Get root directory for repository
117 | *
118 | * @param string $repo
119 | * @return string
120 | */
121 | public function rootDir($repo)
122 | {
123 | return isset($this->_dirs[$repo]) ? $this->_dirs[$repo] : '';
124 | }
125 |
126 | /**
127 | * Get icons directory
128 | *
129 | * @param string $repo
130 | * @return string
131 | */
132 | public function iconsDir($repo)
133 | {
134 | switch ($repo) {
135 | case 'iconify':
136 | $dir = $this->rootDir($repo);
137 | return $dir === '' ? '' : $dir . '/json';
138 |
139 | default:
140 | return $this->rootDir($repo);
141 | }
142 | }
143 |
144 | /**
145 | * Get root directory for repository
146 | *
147 | * @param string $repo
148 | * @param string $time
149 | * @param string $dir
150 | */
151 | public function setRepositoryDir($repo, $time, $dir = '')
152 | {
153 | $this->_versions[$repo] = $time;
154 |
155 | if ($dir === '') {
156 | if ($this->_reposBaseDir === null) {
157 | $this->_getBaseReposDir();
158 | }
159 | $dir = $this->_reposBaseDir . '/' . $repo . '.' . $time;
160 | }
161 |
162 | $this->_setRootDir($repo, $dir);
163 | }
164 |
165 | /**
166 | * Set custom root directory for repository
167 | *
168 | * @param string $repo
169 | * @param string $dir
170 | */
171 | protected function _setRootDir($repo, $dir)
172 | {
173 | $extraKey = $repo . '-dir';
174 | if (isset($this->_config['sync']) && !empty($this->_config['sync'][$extraKey])) {
175 | $extra = $this->_config['sync'][$extraKey];
176 | if (substr($extra, 0, 1) !== '/') {
177 | $extra = '/' . $extra;
178 | }
179 | if (substr($extra, -1) === '/') {
180 | $extra = substr($extra, 0, strlen($extra) - 1);
181 | }
182 | $dir .= $extra;
183 | }
184 | $this->_dirs[$repo] = $dir;
185 | }
186 |
187 | /**
188 | * Get all repositories
189 | *
190 | * @return array
191 | */
192 | public function getRepos()
193 | {
194 | return $this->_repos;
195 | }
196 |
197 | /**
198 | * Get base repositories directory
199 | */
200 | protected function _getBaseReposDir()
201 | {
202 | $this->_reposBaseDir = $this->_config['sync']['storage'];
203 | }
204 |
205 | /**
206 | * Save versions.json
207 | */
208 | public function saveVersions()
209 | {
210 | if ($this->_versions === null) {
211 | return;
212 | }
213 |
214 | if (empty($this->_config['sync']) || empty($this->_config['sync']['secret']) || empty($this->_config['sync']['versions']) || empty($this->_config['sync']['storage'])) {
215 | // Synchronization is inactive
216 | return;
217 | }
218 |
219 | // Make storage directory
220 | @mkdir($this->_config['sync']['storage']);
221 |
222 | // Save versions.json
223 | $filename = $this->_config['sync']['versions'];
224 | @file_put_contents($filename, json_encode($this->_versions, JSON_PRETTY_PRINT));
225 | }
226 |
227 | /**
228 | * Get last repository time for all synchronized repositories
229 | *
230 | * @return array|null
231 | */
232 | public function getLatestRepos()
233 | {
234 | return $this->_versions;
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/lib/Query.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the license.txt
9 | * file that was distributed with this source code.
10 | * @license MIT
11 | */
12 |
13 | namespace Iconify\API;
14 |
15 | use \Iconify\JSONTools\Collection;
16 | use \Iconify\JSONTools\SVG;
17 |
18 | class Query {
19 | /**
20 | * Generate data for query
21 | *
22 | * @param Collection $collection
23 | * @param string $query
24 | * @param string $ext
25 | * @param array $params
26 | * @return int|array
27 | */
28 | public static function parse($collection, $query, $ext, $params)
29 | {
30 | switch ($ext) {
31 | case 'svg':
32 | // Generate SVG
33 | // query = icon name
34 | $icon = $collection->getIconData($query);
35 | if ($icon === null) {
36 | return 404;
37 | }
38 | $svg = new SVG($icon);
39 | $body = $svg->getSVG($params);
40 | return [
41 | 'filename' => $query . '.svg',
42 | 'type' => 'image/svg+xml; charset=utf-8',
43 | 'body' => $body
44 | ];
45 |
46 | case 'js':
47 | case 'json':
48 | if ($query !== 'icons' || !isset($params['icons']) || !is_string($params['icons'])) {
49 | return 404;
50 | }
51 |
52 | $result = $collection->getIcons(explode(',', $params['icons']));
53 |
54 | if ($result === null || empty($result['icons'])) {
55 | return 404;
56 | }
57 | if (isset($result['aliases']) && empty($result['aliases'])) {
58 | unset($result['aliases']);
59 | }
60 | $result = json_encode($result);
61 |
62 | if ($ext === 'js') {
63 | if (isset($params['callback'])) {
64 | if (!preg_match('/^[a-z0-9_.]+$/i', $params['callback'])) {
65 | return 400;
66 | }
67 | $callback = $params['callback'];
68 | } else {
69 | $callback = 'SimpleSVG._loaderCallback';
70 | }
71 | return [
72 | 'type' => 'application/javascript; charset=utf-8',
73 | 'body' => $callback . '(' . $result . ')'
74 | ];
75 | }
76 | return [
77 | 'type' => 'application/json; charset=utf-8',
78 | 'body' => $result
79 | ];
80 |
81 | default:
82 | return 404;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/Registry.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the license.txt
9 | * file that was distributed with this source code.
10 | * @license MIT
11 | */
12 |
13 | namespace Iconify\API;
14 |
15 | use \Iconify\JSONTools\Collection;
16 |
17 | class Registry {
18 | /**
19 | * @var int Version number used to bust old cache
20 | */
21 | protected static $_version = 1;
22 |
23 | /**
24 | * @var null|array List of collections
25 | */
26 | protected $_collections = null;
27 |
28 | /**
29 | * @var string|null
30 | */
31 | protected $_cacheDir;
32 |
33 | /**
34 | * Registry constructor.
35 | *
36 | * @param string $cacheDir
37 | * @param callable $callback
38 | */
39 | public function __construct($cacheDir, $callback)
40 | {
41 | $this->_cacheDir = $cacheDir;
42 | $cacheFile = $cacheDir ? $cacheDir . '/collections-' . self::$_version . '.php' : null;
43 |
44 | if ($cacheFile !== null && @file_exists($cacheFile)) {
45 | // Try to load from cache
46 | $this->_loadFromCache($cacheFile);
47 | if ($this->_collections) {
48 | return;
49 | }
50 | }
51 |
52 | // Get data from callback
53 | $this->_collections = $callback();
54 |
55 | // Save cache
56 | if ($this->_collections && $cacheFile !== null) {
57 | $this->_saveCache($cacheFile);
58 | }
59 | }
60 |
61 | /**
62 | * Get collection
63 | *
64 | * @param string $prefix
65 | * @return null|Collection
66 | */
67 | public function getCollection($prefix)
68 | {
69 | if (!$this->_collections || !isset($this->_collections[$prefix])) {
70 | return null;
71 | }
72 |
73 | $collection = new Collection($prefix);
74 | $collection->loadFromFile($this->_collections[$prefix], null, $this->_cacheDir ? $this->_cacheDir . '/collection-' . self::$_version . '-' . $prefix . '.php' : null);
75 | return $collection;
76 | }
77 |
78 | /**
79 | * Get collection file modification time
80 | *
81 | * @param string $prefix
82 | * @return bool|int
83 | */
84 | public function getCollectionTime($prefix)
85 | {
86 | return @filemtime($this->_cacheDir ? $this->_cacheDir . '/collection-' . self::$_version . '-' . $prefix . '.php' : $this->_collections[$prefix]);
87 | }
88 |
89 | /**
90 | * Load collections list from cache
91 | *
92 | * @param string $filename
93 | */
94 | protected function _loadFromCache($filename)
95 | {
96 | $cache_file = null;
97 | $cache_version = null;
98 | $cached_collections = null;
99 |
100 | try {
101 | /** @noinspection PhpIncludeInspection */
102 | include $filename;
103 | } catch (\Exception $e) {
104 | return;
105 | }
106 |
107 | if (
108 | $cache_file !== $filename ||
109 | $cache_version !== self::$_version ||
110 | $cached_collections === null
111 | ) {
112 | return;
113 | }
114 |
115 | $this->_collections = $cached_collections;
116 | }
117 |
118 | /**
119 | * Save cache
120 | *
121 | * @param string $filename Cache filename
122 | */
123 | protected function _saveCache($filename)
124 | {
125 | $content = "_collections, true) . ";
129 | }";
130 | file_put_contents($filename, $content);
131 | @chmod($filename, 0644);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/lib/Repositories.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the license.txt
9 | * file that was distributed with this source code.
10 | * @license MIT
11 | */
12 |
13 | namespace Iconify\API;
14 |
15 | class Repositories {
16 | protected $_config;
17 |
18 | /**
19 | * Constructor
20 | *
21 | * @param $config
22 | */
23 | public function __construct($config)
24 | {
25 | $this->_config = $config;
26 | }
27 |
28 | /**
29 | * Locate all collections
30 | *
31 | * @return array
32 | */
33 | public function locateCollections()
34 | {
35 | $collections = [];
36 |
37 | $dirs = Dirs::instance($this->_config);
38 | $repos = $dirs->getRepos();
39 |
40 | foreach ($repos as $repo) {
41 | $dir = $dirs->iconsDir($repo);
42 | if ($dir !== '') {
43 | $collections = array_merge($collections, $this->_scanDirectory($dir));
44 | }
45 | }
46 |
47 | return $collections;
48 | }
49 |
50 | /**
51 | * Find all collections in directory
52 | *
53 | * @param string $dir
54 | * @return array
55 | */
56 | protected function _scanDirectory($dir)
57 | {
58 | // List all json files
59 | $collections = [];
60 | $res = @opendir($dir);
61 | if ($res !== null) {
62 | while (($file = readdir($res)) !== false) {
63 | $dot = substr($file, 0, 1);
64 | if ($dot === '.' || $dot === '_') {
65 | continue;
66 | }
67 | $list = explode('.', $file);
68 | if (count($list) !== 2 || $list[1] !== 'json') {
69 | continue;
70 | }
71 | $collections[$list[0]] = $dir . '/' . $file;
72 | }
73 | closedir($res);
74 | }
75 |
76 | return $collections;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/Sync.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the license.txt
9 | * file that was distributed with this source code.
10 | * @license MIT
11 | */
12 |
13 | namespace Iconify\API;
14 |
15 | class Sync {
16 | protected $_config;
17 | protected $_versions;
18 |
19 | /**
20 | * Constructor
21 | *
22 | * @param array $config
23 | */
24 | public function __construct($config)
25 | {
26 | $this->_config = $config;
27 | }
28 |
29 | /**
30 | * Synchronize repository
31 | *
32 | * @param string $repo
33 | * @return bool True on success, false on failure
34 | */
35 | public function sync($repo)
36 | {
37 | $dirs = Dirs::instance($this->_config);
38 | if (!in_array($repo, $dirs->getRepos()) || empty($this->_config['sync'][$repo])) {
39 | return false;
40 | }
41 |
42 | // Clean up old directories
43 | $this->cleanup();
44 |
45 | // Start synchronizing
46 | $time = time();
47 | $url = $this->_config['sync'][$repo];
48 | $target = $this->_config['sync']['storage'] . '/' . $repo . '.' . $time;
49 |
50 | $cmd = strtr($this->_config['sync']['git'], [
51 | '{repo}' => '"' . $url . '"',
52 | '{target}' => '"' . $target . '"'
53 | ]);
54 |
55 | exec($cmd);
56 | if (!is_dir($target)) {
57 | return false;
58 | }
59 |
60 | // Save new version
61 | $dirs->setRepositoryDir($repo, $time);
62 | $dirs->saveVersions();
63 |
64 | $this->_purgeCache();
65 |
66 | return true;
67 | }
68 |
69 | /**
70 | * Purge old cache
71 | */
72 | protected function _purgeCache()
73 | {
74 | $dir = $this->_config['cache-dir'];
75 |
76 | foreach (new \DirectoryIterator($dir) as $entry) {
77 | if (!$entry->isFile()) {
78 | continue;
79 | }
80 |
81 | $file = $entry->getFilename();
82 | $parts = explode('.', $file);
83 |
84 | if (count($parts) < 2 || $file === 'index.php') {
85 | continue;
86 | }
87 |
88 | $ext = array_pop($parts);
89 | if ($ext !== 'php') {
90 | continue;
91 | }
92 |
93 | @unlink($dir . '/' . $file);
94 | }
95 | }
96 |
97 | /**
98 | * Remove old repositories
99 | */
100 | public function cleanup()
101 | {
102 | $base = $this->_config['sync']['storage'];
103 | $dirs = Dirs::instance($this->_config);
104 | $repos = $dirs->getLatestRepos();
105 |
106 | // Find directories that require cleaning
107 | foreach (new \DirectoryIterator($base) as $entry) {
108 | if (!$entry->isDir() || $entry->isDot()) {
109 | continue;
110 | }
111 |
112 | $dir = $entry->getFilename();
113 | $parts = explode('.', $dir);
114 | $repo = $parts[0];
115 | if (count($parts) !== 2 || empty($repos[$repo])) {
116 | continue;
117 | }
118 |
119 | $time = intval($parts[1]);
120 | if ($time > ($repos[$repo] - 3600)) {
121 | // wait 1 hour before deleting old repository
122 | continue;
123 | }
124 |
125 | $this->rmdir($base . '/' . $dir);
126 | }
127 | }
128 |
129 | /**
130 | * Remove directory and contents
131 | *
132 | * @param string $dir
133 | */
134 | protected function rmdir($dir)
135 | {
136 | foreach (new \DirectoryIterator($dir) as $entry) {
137 | if ($entry->isDot()) {
138 | continue;
139 | }
140 | $filename = $dir . '/' . $entry->getFilename();
141 | if ($entry->isDir()) {
142 | $this->rmdir($filename);
143 | } else {
144 | unlink($filename);
145 | }
146 | }
147 | rmdir($dir);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "test": "bin/phpunit"
4 | },
5 | "bugs": "https://github.com/iconify/api.php/issues",
6 | "homepage": "https://github.com/iconify/api.php",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+ssh://git@github.com/iconify/api.php.git"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Iconify API
2 |
3 | This software is no longer maintained. Use Node.js version of API: https://github.com/iconify/api
4 |
5 | PHP is no longer supported because:
6 |
7 | - It is much slower than Node version of API.
8 | - It has not been maintained for a while.
9 | - Updating it would require large effort, but it never be able to match Node version because of import and cleanup tools available in Node.
10 |
11 | ## About
12 |
13 | This code runs on api.iconify.design that is used to serve collections and SVG images.
14 |
15 | Make sure your server matches requirements below:
16 |
17 | - You must have PHP 5.6 or newer version.
18 | - Server must be running Apache with mod_rewrite enabled and AllowOverride enabled.
19 | - Files must be writable by same user that runs PHP scripts to allow writing to "cache" and "git-repos" directories.
20 | - If you are using function to synchronize repositories, make sure Git is installed and is accessable from command line.
21 |
22 | Node.js version is available at https://github.com/iconify/api.js
23 |
24 | ### How to use it
25 |
26 | Upload files in root directory of your website. Icons server requires its own domain or sub-domain.
27 |
28 | Add custom configuration to config.php. Default configuration is available as reference in config-default.php. See [config.md](config.md)
29 |
30 | ### Node vs PHP
31 |
32 | Node.js version of server is faster because it loads everything only once on startup. It is a bit harder to setup though because you need to install additional software and make sure server is running (using tools such as "pm2").
33 |
34 | PHP process ends when HTTP request ends, so PHP has to reload lots of things for each request. PHP version has caching to minimize loading times, but it is still nowhere near as fast as Node.js version. The only upside of PHP version is it is easy to setup - simply upload files and you are done.
35 |
--------------------------------------------------------------------------------
/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
--------------------------------------------------------------------------------
/tests/query_test.php:
--------------------------------------------------------------------------------
1 | _collection1 = new Collection('test');
13 | $this->_collection1->loadJSON([
14 | 'prefix' => 'test',
15 | 'icons' => [
16 | 'icon1' => [
17 | 'body' => '',
18 | 'width' => 30
19 | ],
20 | 'icon2' => [
21 | 'body' => ''
22 | ]
23 | ],
24 | 'aliases' => [
25 | 'alias1' => [
26 | 'parent' => 'icon2',
27 | 'hFlip' => true
28 | ]
29 | ],
30 | 'width' => 24,
31 | 'height' => 24
32 | ]);
33 |
34 | $this->_collection2 = new Collection('test2');
35 | $this->_collection2->loadJSON([
36 | 'icons' => [
37 | 'test2-icon1' => [
38 | 'body' => '',
39 | 'width' => 30
40 | ],
41 | 'test2-icon2' => [
42 | 'body' => ''
43 | ],
44 | 'test2-icon3' => [
45 | 'body' => ''
46 | ]
47 | ],
48 | 'aliases' => [
49 | 'test2-alias1' => [
50 | 'parent' => 'test2-icon2',
51 | 'hFlip' => true
52 | ]
53 | ],
54 | 'width' => 24,
55 | 'height' => 24
56 | ]);
57 | }
58 |
59 | public function testIconsList()
60 | {
61 | $this->assertEquals([
62 | 'type' => 'application/javascript; charset=utf-8',
63 | 'body' => 'SimpleSVG._loaderCallback({"prefix":"test","icons":{"icon2":{"body":"","width":24,"height":24}},"aliases":{"alias1":{"parent":"icon2","hFlip":true}}})'
64 | ], Query::parse($this->_collection1, 'icons', 'js', [
65 | 'icons' => 'alias1'
66 | ]));
67 |
68 | // Query collection without prefix, json
69 | $this->assertEquals([
70 | 'type' => 'application/json; charset=utf-8',
71 | 'body' => '{"prefix":"test2","icons":{"icon2":{"body":"","width":24,"height":24}},"aliases":{"alias1":{"parent":"icon2","hFlip":true}}}'
72 | ], Query::parse($this->_collection2, 'icons', 'json', [
73 | 'icons' => 'alias1'
74 | ]));
75 |
76 | // Custom callback
77 | $this->assertEquals([
78 | 'type' => 'application/javascript; charset=utf-8',
79 | 'body' => 'console.log({"prefix":"test","icons":{"icon1":{"body":"","width":30,"height":24},"icon2":{"body":"","width":24,"height":24}}})'
80 | ], Query::parse($this->_collection1, 'icons', 'js', [
81 | 'icons' => 'icon1,icon2',
82 | 'callback' => 'console.log'
83 | ]));
84 | }
85 |
86 | public function testSVG()
87 | {
88 | // Simple icon
89 | $this->assertEquals([
90 | 'filename' => 'icon1.svg',
91 | 'type' => 'image/svg+xml; charset=utf-8',
92 | 'body' => ''
93 | ], Query::parse($this->_collection1, 'icon1', 'svg', []));
94 |
95 | // Icon with custom attributes
96 | $this->assertEquals([
97 | 'filename' => 'alias1.svg',
98 | 'type' => 'image/svg+xml; charset=utf-8',
99 | 'body' => ''
100 | ], Query::parse($this->_collection1, 'alias1', 'svg', [
101 | 'color' => 'red'
102 | ]));
103 |
104 | // Icon with id replacement
105 | $result = Query::parse($this->_collection2, 'icon3', 'svg', [
106 | 'color' => 'red',
107 | 'rotate' => '90deg'
108 | ]);
109 | $result = preg_replace('/IconifyId-[0-9a-f]+-[0-9a-f]+-[0-9]+/', 'some-id', $result['body']);
110 | $this->assertEquals('', $result);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------