1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* League.Uri (https://uri.thephpleague.com) |
5
|
|
|
* |
6
|
|
|
* (c) Ignace Nyamagana Butera <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
declare(strict_types=1); |
13
|
|
|
|
14
|
|
|
namespace LeagueTest\Uri; |
15
|
|
|
|
16
|
|
|
use League\Uri\Exceptions\SyntaxError; |
17
|
|
|
use League\Uri\Exceptions\TemplateCanNotBeExpanded; |
18
|
|
|
use League\Uri\UriTemplate; |
19
|
|
|
use PHPUnit\Framework\TestCase; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @coversDefaultClass \League\Uri\UriTemplate |
23
|
|
|
*/ |
24
|
|
|
final class UriTemplateTest extends TestCase |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @covers ::__construct |
28
|
|
|
* @covers ::getTemplate |
29
|
|
|
*/ |
30
|
|
|
public function testGetTemplate(): void |
31
|
|
|
{ |
32
|
|
|
$template = 'http://example.com{+path}{/segments}{?query,more*,foo[]*}'; |
33
|
|
|
$variables = [ |
34
|
|
|
'path' => '/foo/bar', |
35
|
|
|
'segments' => ['one', 'two'], |
36
|
|
|
'query' => 'test', |
37
|
|
|
'more' => ['fun', 'ice cream'], |
38
|
|
|
'foo[]' => ['fizz', 'buzz'], |
39
|
|
|
]; |
40
|
|
|
|
41
|
|
|
$uriTemplate = new UriTemplate($template, $variables); |
42
|
|
|
|
43
|
|
|
self::assertSame($template, $uriTemplate->getTemplate()); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @covers ::__construct |
48
|
|
|
* @covers ::filterVariables |
49
|
|
|
* @covers ::getDefaultVariables |
50
|
|
|
*/ |
51
|
|
|
public function testGetDefaultVariables(): void |
52
|
|
|
{ |
53
|
|
|
$template = 'http://example.com{+path}{/segments}{?query,more*,foo[]*}'; |
54
|
|
|
$variables = [ |
55
|
|
|
'path' => '/foo/bar', |
56
|
|
|
'segments' => ['one', 'two', 3, true, 'false', false, null], |
57
|
|
|
'query' => 'test', |
58
|
|
|
'more' => ['fun', 'ice cream'], |
59
|
|
|
'foo[]' => ['fizz', 'buzz'], |
60
|
|
|
'nonexistent' => ['random'], |
61
|
|
|
]; |
62
|
|
|
|
63
|
|
|
$expectedVariables = [ |
64
|
|
|
'path' => '/foo/bar', |
65
|
|
|
'segments' => ['one', 'two', '3', '1', 'false', '0', ''], |
66
|
|
|
'query' => 'test', |
67
|
|
|
'more' => ['fun', 'ice cream'], |
68
|
|
|
'foo[]' => ['fizz', 'buzz'], |
69
|
|
|
]; |
70
|
|
|
|
71
|
|
|
$uriTemplate = new UriTemplate($template, $variables); |
72
|
|
|
self::assertSame($expectedVariables, $uriTemplate->getDefaultVariables()); |
73
|
|
|
|
74
|
|
|
$uriTemplateEmpty = new UriTemplate($template, []); |
75
|
|
|
self::assertSame([], $uriTemplateEmpty->getDefaultVariables()); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @covers ::filterVariables |
80
|
|
|
* @covers ::withDefaultVariables |
81
|
|
|
*/ |
82
|
|
|
public function testWithDefaultVariables(): void |
83
|
|
|
{ |
84
|
|
|
$template = '{foo}{bar}'; |
85
|
|
|
$variables = ['foo' => 'foo', 'bar' => 'bar']; |
86
|
|
|
$newVariables = ['foo' => 'bar', 'bar' => 'foo']; |
87
|
|
|
$newAltVariables = ['foo' => 'foo', 'bar' => 'bar', 'filteredVariable' => 'random']; |
88
|
|
|
|
89
|
|
|
$uriTemplate = new UriTemplate($template, $variables); |
90
|
|
|
$newTemplate = $uriTemplate->withDefaultVariables($newVariables); |
91
|
|
|
$altTemplate = $uriTemplate->withDefaultVariables($variables); |
92
|
|
|
$newAltTemplate = $uriTemplate->withDefaultVariables($newAltVariables); |
93
|
|
|
|
94
|
|
|
self::assertSame($altTemplate->getDefaultVariables(), $uriTemplate->getDefaultVariables()); |
95
|
|
|
self::assertSame($newAltTemplate->getDefaultVariables(), $uriTemplate->getDefaultVariables()); |
96
|
|
|
self::assertNotSame($newTemplate->getDefaultVariables(), $uriTemplate->getDefaultVariables()); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @covers ::__set_state |
101
|
|
|
*/ |
102
|
|
|
public function testSetState(): void |
103
|
|
|
{ |
104
|
|
|
$template = '{foo}{bar}'; |
105
|
|
|
$variables = ['foo' => 'foo', 'bar' => 'bar']; |
106
|
|
|
|
107
|
|
|
$uriTemplate = new UriTemplate($template, $variables); |
108
|
|
|
|
109
|
|
|
self::assertEquals($uriTemplate, eval('return '.var_export($uriTemplate, true).';')); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @covers ::getVariableNames |
114
|
|
|
* |
115
|
|
|
* @dataProvider expectedVariableNames |
116
|
|
|
*/ |
117
|
|
|
public function testGetVariableNames(string $template, array $expected): void |
118
|
|
|
{ |
119
|
|
|
self::assertSame($expected, (new UriTemplate($template))->getVariableNames()); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
View Code Duplication |
public function expectedVariableNames(): iterable |
|
|
|
|
123
|
|
|
{ |
124
|
|
|
return [ |
125
|
|
|
[ |
126
|
|
|
'template' => '', |
127
|
|
|
'expected' => [], |
128
|
|
|
], |
129
|
|
|
[ |
130
|
|
|
'template' => '{foo}{bar}', |
131
|
|
|
'expected' => ['foo', 'bar'], |
132
|
|
|
], |
133
|
|
|
[ |
134
|
|
|
'template' => '{foo}{foo:2}{+foo}', |
135
|
|
|
'expected' => ['foo'], |
136
|
|
|
], |
137
|
|
|
[ |
138
|
|
|
'template' => '{bar}{foo}', |
139
|
|
|
'expected' => ['bar', 'foo'], |
140
|
|
|
], |
141
|
|
|
]; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @covers ::expand |
146
|
|
|
* |
147
|
|
|
* @dataProvider templateExpansionProvider |
148
|
|
|
*/ |
149
|
|
|
public function testExpandsUriTemplates(string $template, string $expectedUriString, array $variables): void |
150
|
|
|
{ |
151
|
|
|
self::assertSame($expectedUriString, (new UriTemplate($template))->expand($variables)->__toString()); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
public function templateExpansionProvider(): iterable |
155
|
|
|
{ |
156
|
|
|
$variables = [ |
157
|
|
|
'var' => 'value', |
158
|
|
|
'hello' => 'Hello World!', |
159
|
|
|
'empty' => '', |
160
|
|
|
'path' => '/foo/bar', |
161
|
|
|
'x' => '1024', |
162
|
|
|
'y' => '768', |
163
|
|
|
'null' => null, |
164
|
|
|
'list' => ['red', 'green', 'blue'], |
165
|
|
|
'keys' => [ |
166
|
|
|
'semi' => ';', |
167
|
|
|
'dot' => '.', |
168
|
|
|
'comma' => ',', |
169
|
|
|
], |
170
|
|
|
'empty_keys' => [], |
171
|
|
|
'bool' => true, |
172
|
|
|
]; |
173
|
|
|
|
174
|
|
|
$templateAndExpansionData = [ |
175
|
|
|
'level 1' => [ |
176
|
|
|
['foo', 'foo'], |
177
|
|
|
['{var}', 'value'], |
178
|
|
|
['{hello}', 'Hello%20World%21'], |
179
|
|
|
['{bool}', '1'], |
180
|
|
|
], |
181
|
|
|
'level 2' => [ |
182
|
|
|
['{+var}', 'value'], |
183
|
|
|
['{+hello}', 'Hello%20World!'], |
184
|
|
|
['{+path}/here', '/foo/bar/here'], |
185
|
|
|
['here?ref={+path}', 'here?ref=/foo/bar'], |
186
|
|
|
], |
187
|
|
|
'level 3' => [ |
188
|
|
|
['X{#var}', 'X#value'], |
189
|
|
|
['X{#hello}', 'X#Hello%20World!'], |
190
|
|
|
['map?{x,y}', 'map?1024,768'], |
191
|
|
|
['{x,hello,y}', '1024,Hello%20World%21,768'], |
192
|
|
|
['{+x,hello,y}', '1024,Hello%20World!,768'], |
193
|
|
|
['{+path,x}/here', '/foo/bar,1024/here'], |
194
|
|
|
['{#x,hello,y}', '#1024,Hello%20World!,768'], |
195
|
|
|
['{#path,x}/here', '#/foo/bar,1024/here'], |
196
|
|
|
['X{.var}', 'X.value'], |
197
|
|
|
['X{.x,y}', 'X.1024.768'], |
198
|
|
|
['{/var}', '/value'], |
199
|
|
|
['{/var,x}/here', '/value/1024/here'], |
200
|
|
|
['{;x,y}', ';x=1024;y=768'], |
201
|
|
|
['{;x,y,empty}', ';x=1024;y=768;empty'], |
202
|
|
|
['{?x,y}', '?x=1024&y=768'], |
203
|
|
|
['{?x,y,empty}', '?x=1024&y=768&empty='], |
204
|
|
|
['{?x,y,undef}', '?x=1024&y=768'], |
205
|
|
|
['?fixed=yes{&x}', '?fixed=yes&x=1024'], |
206
|
|
|
['{&x,y,empty}', '&x=1024&y=768&empty='], |
207
|
|
|
], |
208
|
|
|
'level 4' => [ |
209
|
|
|
['{var:3}', 'val'], |
210
|
|
|
['{var:30}', 'value'], |
211
|
|
|
['{list}', 'red,green,blue'], |
212
|
|
|
['{list*}', 'red,green,blue'], |
213
|
|
|
['{keys}', 'semi,%3B,dot,.,comma,%2C'], |
214
|
|
|
['{keys*}', 'semi=%3B,dot=.,comma=%2C'], |
215
|
|
|
['{+path:6}/here', '/foo/b/here'], |
216
|
|
|
['{+list}', 'red,green,blue'], |
217
|
|
|
['{+list*}', 'red,green,blue'], |
218
|
|
|
['{+keys}', 'semi,;,dot,.,comma,,'], |
219
|
|
|
['{+keys*}', 'semi=;,dot=.,comma=,'], |
220
|
|
|
['{#path:6}/here', '#/foo/b/here'], |
221
|
|
|
['{#list}', '#red,green,blue'], |
222
|
|
|
['{#list*}', '#red,green,blue'], |
223
|
|
|
['{#keys}', '#semi,;,dot,.,comma,,'], |
224
|
|
|
['{#keys*}', '#semi=;,dot=.,comma=,'], |
225
|
|
|
['X{.var:3}', 'X.val'], |
226
|
|
|
['X{.list}', 'X.red,green,blue'], |
227
|
|
|
['X{.list*}', 'X.red.green.blue'], |
228
|
|
|
['X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'], |
229
|
|
|
['X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'], |
230
|
|
|
['{/var:1,var}', '/v/value'], |
231
|
|
|
['{/list}', '/red,green,blue'], |
232
|
|
|
['{/list*}', '/red/green/blue'], |
233
|
|
|
['{/list*,path:4}', '/red/green/blue/%2Ffoo'], |
234
|
|
|
['{/keys}', '/semi,%3B,dot,.,comma,%2C'], |
235
|
|
|
['{/keys*}', '/semi=%3B/dot=./comma=%2C'], |
236
|
|
|
['{;hello:5}', ';hello=Hello'], |
237
|
|
|
['{;list}', ';list=red,green,blue'], |
238
|
|
|
['{;list*}', ';list=red;list=green;list=blue'], |
239
|
|
|
['{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'], |
240
|
|
|
['{;keys*}', ';semi=%3B;dot=.;comma=%2C'], |
241
|
|
|
['{?var:3}', '?var=val'], |
242
|
|
|
['{?list}', '?list=red,green,blue'], |
243
|
|
|
['{?list*}', '?list=red&list=green&list=blue'], |
244
|
|
|
['{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'], |
245
|
|
|
['{?keys*}', '?semi=%3B&dot=.&comma=%2C'], |
246
|
|
|
['{&var:3}', '&var=val'], |
247
|
|
|
['{&list}', '&list=red,green,blue'], |
248
|
|
|
['{&list*}', '&list=red&list=green&list=blue'], |
249
|
|
|
['{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'], |
250
|
|
|
['{&keys*}', '&semi=%3B&dot=.&comma=%2C'], |
251
|
|
|
['{.null}', ''], |
252
|
|
|
['{.null,var}', '.value'], |
253
|
|
|
['X{.empty_keys*}', 'X'], |
254
|
|
|
['X{.empty_keys}', 'X'], |
255
|
|
|
], |
256
|
|
|
'extra' => [ |
257
|
|
|
// Test that missing expansions are skipped |
258
|
|
|
['test{&missing*}', 'test'], |
259
|
|
|
// Test that multiple expansions can be set |
260
|
|
|
['http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'], |
261
|
|
|
// Test more complex query string stuff |
262
|
|
|
['http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C'], |
263
|
|
|
], |
264
|
|
|
]; |
265
|
|
|
|
266
|
|
View Code Duplication |
foreach ($templateAndExpansionData as $specification => $tests) { |
|
|
|
|
267
|
|
|
foreach ($tests as $offset => $test) { |
268
|
|
|
yield $specification.' test '.$offset => [ |
269
|
|
|
'template' => $test[0], |
270
|
|
|
'expectedUriString' => $test[1], |
271
|
|
|
'variables' => $variables, |
272
|
|
|
]; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
public function testAllowsQueryValuePairsArrayExpansion(): void |
278
|
|
|
{ |
279
|
|
|
$template = 'http://example.com{+path}{/segments}{?query,more*,foo[]*}'; |
280
|
|
|
$variables = [ |
281
|
|
|
'path' => '/foo/bar', |
282
|
|
|
'segments' => ['one', 'two'], |
283
|
|
|
'query' => 'test', |
284
|
|
|
'more' => ['fun', 'ice cream'], |
285
|
|
|
'foo[]' => ['fizz', 'buzz'], |
286
|
|
|
]; |
287
|
|
|
|
288
|
|
|
self::assertSame( |
289
|
|
|
'http://example.com/foo/bar/one,two?query=test&more=fun&more=ice%20cream&foo%5B%5D=fizz&foo%5B%5D=buzz', |
290
|
|
|
(new UriTemplate($template))->expand($variables)->__toString() |
291
|
|
|
); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* @covers \League\Uri\Exceptions\TemplateCanNotBeExpanded |
296
|
|
|
* @covers \League\Uri\UriTemplate\Template |
297
|
|
|
*/ |
298
|
|
|
public function testDisallowNestedArrayExpansion(): void |
299
|
|
|
{ |
300
|
|
|
$template = 'http://example.com{?query,data*,foo*}'; |
301
|
|
|
$variables = [ |
302
|
|
|
'query' => 'test', |
303
|
|
|
'data' => [ |
304
|
|
|
'more' => ['fun', 'ice cream'], |
305
|
|
|
], |
306
|
|
|
'foo' => [ |
307
|
|
|
'baz' => [ |
308
|
|
|
'bar' => 'fizz', |
309
|
|
|
'test' => 'buzz', |
310
|
|
|
], |
311
|
|
|
'bam' => 'boo', |
312
|
|
|
], |
313
|
|
|
]; |
314
|
|
|
|
315
|
|
|
self::expectException(TemplateCanNotBeExpanded::class); |
316
|
|
|
|
317
|
|
|
(new UriTemplate($template))->expand($variables); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @covers ::expand |
322
|
|
|
* @covers ::filterVariables |
323
|
|
|
* @covers \League\Uri\UriTemplate\Template |
324
|
|
|
*/ |
325
|
|
View Code Duplication |
public function testExpandWithDefaultVariables(): void |
|
|
|
|
326
|
|
|
{ |
327
|
|
|
$template = 'http://example.com{+path}{/segments}{?query,more*,foo[]*}'; |
328
|
|
|
|
329
|
|
|
$defaultVariables = [ |
330
|
|
|
'path' => '/foo/bar', |
331
|
|
|
'segments' => ['one', 'two'], |
332
|
|
|
]; |
333
|
|
|
|
334
|
|
|
$variables = [ |
335
|
|
|
'query' => 'test', |
336
|
|
|
'more' => ['fun', 'ice cream'], |
337
|
|
|
'foo[]' => ['fizz', 'buzz'], |
338
|
|
|
]; |
339
|
|
|
|
340
|
|
|
self::assertSame( |
341
|
|
|
'http://example.com/foo/bar/one,two?query=test&more=fun&more=ice%20cream&foo%5B%5D=fizz&foo%5B%5D=buzz', |
342
|
|
|
(new UriTemplate($template, $defaultVariables))->expand($variables)->__toString() |
343
|
|
|
); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* @covers ::expand |
348
|
|
|
* @covers ::filterVariables |
349
|
|
|
* @covers \League\Uri\UriTemplate\Template |
350
|
|
|
*/ |
351
|
|
View Code Duplication |
public function testExpandWithDefaultVariablesWithOverride(): void |
|
|
|
|
352
|
|
|
{ |
353
|
|
|
$template = 'http://example.com{+path}{/segments}{?query,more*,foo[]*}'; |
354
|
|
|
|
355
|
|
|
$defaultVariables = [ |
356
|
|
|
'path' => '/foo/bar', |
357
|
|
|
'segments' => ['one', 'two'], |
358
|
|
|
]; |
359
|
|
|
|
360
|
|
|
$variables = [ |
361
|
|
|
'path' => '/bar/baz', |
362
|
|
|
'query' => 'test', |
363
|
|
|
'more' => ['fun', 'ice cream'], |
364
|
|
|
'foo[]' => ['fizz', 'buzz'], |
365
|
|
|
]; |
366
|
|
|
|
367
|
|
|
self::assertSame( |
368
|
|
|
'http://example.com/bar/baz/one,two?query=test&more=fun&more=ice%20cream&foo%5B%5D=fizz&foo%5B%5D=buzz', |
369
|
|
|
(new UriTemplate($template, $defaultVariables))->expand($variables)->__toString() |
370
|
|
|
); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* @covers \League\Uri\UriTemplate\Template |
375
|
|
|
* |
376
|
|
|
* @dataProvider provideInvalidTemplate |
377
|
|
|
*/ |
378
|
|
|
public function testInvalidUriTemplate(string $template): void |
379
|
|
|
{ |
380
|
|
|
self::expectException(SyntaxError::class); |
381
|
|
|
|
382
|
|
|
new UriTemplate($template); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* @see https://github.com/uri-templates/uritemplate-test/blob/master/negative-tests.json |
387
|
|
|
*/ |
388
|
|
|
public function provideInvalidTemplate(): iterable |
389
|
|
|
{ |
390
|
|
|
return [ |
391
|
|
|
'mismatch in at least one expression (1)' => ['http://example.com/}/{+foo}'], |
392
|
|
|
'mismatch in at least one expression (2)' => ['http://example.com/{/{+foo}'], |
393
|
|
|
]; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* @covers \League\Uri\UriTemplate\Template |
398
|
|
|
*/ |
399
|
|
|
public function testExpansionWithMultipleSameExpression(): void |
400
|
|
|
{ |
401
|
|
|
$template = '{foo}/{foo}'; |
402
|
|
|
$data = ['foo' => 'foo']; |
403
|
|
|
|
404
|
|
|
self::assertSame('foo/foo', (new UriTemplate($template, $data))->expand()->__toString()); |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.