1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Boudewijn Schoon <[email protected]> |
4
|
|
|
* @copyright Zicht Online <http://zicht.nl> |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Zicht\Itertools\twig; |
8
|
|
|
|
9
|
|
|
use Zicht\Itertools; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Twig extension. |
13
|
|
|
* |
14
|
|
|
* <service id="zicht_itertools_twig_extension" class="Zicht\Itertools\twig\Extension"> |
15
|
|
|
* <tag name="twig.extension"/> |
16
|
|
|
* </service> |
17
|
|
|
* |
18
|
|
|
* @package Zicht\Itertools\twig |
19
|
|
|
*/ |
20
|
|
|
class Extension extends \Twig_Extension |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* @{inheritDoc} |
24
|
|
|
*/ |
25
|
1 |
|
public function getFilters() |
26
|
|
|
{ |
27
|
|
|
return [ |
28
|
|
|
// filter names are case-sensitive |
29
|
1 |
|
new \Twig_SimpleFilter('all', '\Zicht\Itertools\all'), |
30
|
1 |
|
new \Twig_SimpleFilter('any', '\Zicht\Itertools\any'), |
31
|
1 |
|
new \Twig_SimpleFilter('chain', '\Zicht\Itertools\chain'), |
32
|
1 |
|
new \Twig_SimpleFilter('filter', [$this, 'filter']), |
33
|
1 |
|
new \Twig_SimpleFilter('first', '\Zicht\Itertools\first'), |
34
|
1 |
|
new \Twig_SimpleFilter('group_by', [$this, 'groupBy']), |
35
|
1 |
|
new \Twig_SimpleFilter('last', '\Zicht\Itertools\last'), |
36
|
1 |
|
new \Twig_SimpleFilter('map', [$this, 'map']), |
37
|
1 |
|
new \Twig_SimpleFilter('map_by', [$this, 'mapBy']), |
38
|
1 |
|
new \Twig_SimpleFilter('reduce', '\Zicht\Itertools\reduce'), |
39
|
1 |
|
new \Twig_SimpleFilter('reversed', '\Zicht\Itertools\reversed'), |
40
|
1 |
|
new \Twig_SimpleFilter('sorted', [$this, 'sorted']), |
41
|
1 |
|
new \Twig_SimpleFilter('unique', [$this, 'unique']), |
42
|
1 |
|
new \Twig_SimpleFilter('zip', '\Zicht\Itertools\zip'), |
43
|
|
|
|
44
|
|
|
// deprecated filters |
45
|
1 |
|
new \Twig_SimpleFilter('filterby', [$this, 'deprecatedFilterBy'], ['deprecated' => true, 'alternative' => 'filter']), |
46
|
1 |
|
new \Twig_SimpleFilter('groupBy', [$this, 'deprecatedGroupBy'], ['deprecated' => true, 'alternative' => 'group_by']), |
47
|
1 |
|
new \Twig_SimpleFilter('groupby', [$this, 'deprecatedGroupBy'], ['deprecated' => true, 'alternative' => 'group_by']), |
48
|
1 |
|
new \Twig_SimpleFilter('mapBy', [$this, 'deprecatedMapBy'], ['deprecated' => true, 'alternative' => 'map_by']), |
49
|
1 |
|
new \Twig_SimpleFilter('mapby', [$this, 'deprecatedMapBy'], ['deprecated' => true, 'alternative' => 'map_by']), |
50
|
1 |
|
new \Twig_SimpleFilter('sum', [$this, 'deprecatedSum'], ['deprecated' => true, 'alternative' => 'reduce']), |
51
|
1 |
|
new \Twig_SimpleFilter('uniqueby', [$this, 'deprecatedUniqueBy'], ['deprecated' => true, 'alternative' => 'unique']), |
52
|
1 |
|
]; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @{inheritDoc} |
57
|
|
|
*/ |
58
|
1 |
|
public function getFunctions() |
59
|
|
|
{ |
60
|
|
|
return [ |
61
|
1 |
|
new \Twig_SimpleFunction('chain', '\Zicht\Itertools\chain'), |
62
|
1 |
|
new \Twig_SimpleFunction('first', '\Zicht\Itertools\first'), |
63
|
1 |
|
new \Twig_SimpleFunction('last', '\Zicht\Itertools\last'), |
64
|
|
|
|
65
|
|
|
// functions to create closures |
66
|
1 |
|
new \Twig_SimpleFunction('reducing', [$this, 'reducing']), |
67
|
1 |
|
new \Twig_SimpleFunction('mapping', [$this, 'mapping']), |
68
|
1 |
|
new \Twig_SimpleFunction('filtering', [$this, 'filtering']), |
69
|
|
|
|
70
|
|
|
// deprecated functions |
71
|
1 |
|
new \Twig_SimpleFunction('reduction', [$this, 'deprecatedGetReduction'], ['deprecated' => true, 'alternative' => 'reducing']), |
72
|
1 |
|
]; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Takes an iterable and returns another iterable that is unique. |
77
|
|
|
* |
78
|
|
|
* @param array|string|\Iterator $iterable |
79
|
|
|
* @param mixed $strategy |
80
|
|
|
* @return Itertools\lib\UniqueIterator |
81
|
|
|
*/ |
82
|
1 |
|
public function unique($iterable, $strategy = null) |
83
|
|
|
{ |
84
|
1 |
|
return Itertools\unique($strategy, $iterable); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Reduce an iterable to a single value |
89
|
|
|
* |
90
|
|
|
* Simple examples: |
91
|
|
|
* {{ [1,2,3]|reduce }} --> 6 |
92
|
|
|
* {{ [1,2,3]|reduce('max') }} --> 3 |
93
|
|
|
* |
94
|
|
|
* Sro example to get the prices for all items in the basket: |
95
|
|
|
* {{ transaction_snapshot.Basket.Items|map('TotalPrice.Amount')|reduce }} |
96
|
|
|
* |
97
|
|
|
* @param array|string|\Iterator $iterable |
98
|
|
|
* @param string|\Closure $closure |
99
|
|
|
* @param mixed $initializer |
100
|
|
|
* @return mixed |
101
|
|
|
*/ |
102
|
1 |
|
public function reduce($iterable, $closure = 'add', $initializer = null) |
103
|
|
|
{ |
104
|
1 |
|
return Itertools\reduce($iterable, $closure, $initializer); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Make an iterator that returns consecutive groups from the |
109
|
|
|
* $iterable. Generally, the $iterable needs to already be sorted on |
110
|
|
|
* the same key function. |
111
|
|
|
* |
112
|
|
|
* @param array|string|\Iterator $iterable |
113
|
|
|
* @param string|\Closure $strategy |
114
|
|
|
* @param boolean $sort |
115
|
|
|
* @return Itertools\lib\GroupbyIterator |
116
|
|
|
*/ |
117
|
1 |
|
public function groupBy($iterable, $strategy, $sort = true) |
118
|
|
|
{ |
119
|
1 |
|
return Itertools\group_by($strategy, $iterable, $sort); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Make an iterator that returns values from $iterable where the |
124
|
|
|
* $strategy determines that the values are not empty. |
125
|
|
|
* |
126
|
|
|
* @param array|string|\Iterator $iterable |
127
|
|
|
* @param null $strategy |
128
|
|
|
* @return Itertools\lib\FilterIterator |
129
|
|
|
*/ |
130
|
1 |
|
public function filter($iterable, $strategy = null) |
131
|
|
|
{ |
132
|
1 |
|
return Itertools\filter($strategy, $iterable); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Make an iterator that returns the values from $iterable sorted by |
137
|
|
|
* $strategy. |
138
|
|
|
* |
139
|
|
|
* @param array|string|\Iterator $iterable |
140
|
|
|
* @param string|\Closure $strategy |
141
|
|
|
* @param bool $reverse |
142
|
|
|
* @return Itertools\lib\SortedIterator |
143
|
|
|
*/ |
144
|
1 |
|
public function sorted($iterable, $strategy = null, $reverse = false) |
145
|
|
|
{ |
146
|
1 |
|
return Itertools\sorted($strategy, $iterable, $reverse); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Make an iterator that applies $func to every entry in the $iterables. |
151
|
|
|
* |
152
|
|
|
* @param array|string|\Iterator $iterable |
153
|
|
|
* @param string|\Closure $strategy |
154
|
|
|
* @return Itertools\lib\MapIterator |
155
|
|
|
*/ |
156
|
1 |
|
public function map($iterable, $strategy) |
157
|
|
|
{ |
158
|
1 |
|
return Itertools\map($strategy, $iterable); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Make an iterator returning values from $iterable and keys from |
163
|
|
|
* $strategy. |
164
|
|
|
* |
165
|
|
|
* @param array|string|\Iterator $iterable |
166
|
|
|
* @param string|\Closure $strategy |
167
|
|
|
* @return Itertools\lib\MapByIterator |
168
|
|
|
*/ |
169
|
1 |
|
public function mapBy($iterable, $strategy) |
170
|
|
|
{ |
171
|
1 |
|
return Itertools\map_by($strategy, $iterable); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Returns a reduction closure |
177
|
|
|
* |
178
|
|
|
* Any parameters provided, beyond $name, are passed directly to the underlying |
179
|
|
|
* reduction. This can be used to, for example, provide a $glue when using join. |
180
|
|
|
* |
181
|
|
|
* @param string $name |
182
|
|
|
* @return \Closure |
183
|
|
|
* @throws \InvalidArgumentException |
184
|
|
|
*/ |
185
|
4 |
View Code Duplication |
public function reducing($name) |
|
|
|
|
186
|
|
|
{ |
187
|
|
|
// note, once we stop supporting php 5.5, we can rewrite the code below |
188
|
|
|
// to the reducing($name, ...$args) structure. |
|
|
|
|
189
|
|
|
// http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list |
190
|
|
|
|
191
|
4 |
|
if (is_string($name) && in_array($name, [ |
192
|
4 |
|
'add', |
193
|
4 |
|
'chain', |
194
|
4 |
|
'join', |
195
|
4 |
|
'max', |
196
|
4 |
|
'min', |
197
|
4 |
|
'mul', |
198
|
4 |
|
'sub', |
199
|
4 |
|
])) { |
200
|
2 |
|
return call_user_func_array(sprintf('\Zicht\Itertools\reductions\%s', $name), array_slice(func_get_args(), 1)); |
201
|
|
|
} |
202
|
|
|
|
203
|
2 |
|
throw new \InvalidArgumentException(sprintf('$NAME "%s" is not a valid reduction.', $name)); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Returns a mapping closure |
208
|
|
|
* |
209
|
|
|
* Any parameters provided, beyond $name, are passed directly to the underlying |
210
|
|
|
* mapping. This can be used to, for example, provide a $glue when using join. |
211
|
|
|
* |
212
|
|
|
* @param string $name |
213
|
|
|
* @return \Closure |
214
|
|
|
* @throws \InvalidArgumentException |
215
|
|
|
*/ |
216
|
2 |
|
public function mapping($name) |
217
|
|
|
{ |
218
|
|
|
// note, once we stop supporting php 5.5, we can rewrite the code below |
219
|
|
|
// to the reducing($name, ...$args) structure. |
|
|
|
|
220
|
|
|
// http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list |
221
|
|
|
|
222
|
2 |
|
if (is_string($name) && in_array($name, [ |
223
|
2 |
|
'json_decode', |
224
|
2 |
|
'json_encode', |
225
|
2 |
|
'key', |
226
|
2 |
|
'length', |
227
|
2 |
|
'lower', |
228
|
2 |
|
'lstrip', |
229
|
2 |
|
'random', |
230
|
2 |
|
'rstrip', |
231
|
2 |
|
'select', |
232
|
2 |
|
'strip', |
233
|
2 |
|
'type', |
234
|
2 |
|
'upper', |
235
|
2 |
|
])) { |
236
|
1 |
|
return call_user_func_array(sprintf('\Zicht\Itertools\mappings\%s', $name), array_slice(func_get_args(), 1)); |
237
|
|
|
} |
238
|
|
|
|
239
|
1 |
|
throw new \InvalidArgumentException(sprintf('$NAME "%s" is not a valid mapping.', $name)); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Returns a filter closure |
244
|
|
|
* |
245
|
|
|
* Any parameters provided, beyond $name, are passed directly to the underlying |
246
|
|
|
* filter. This can be used to, for example, provide a $glue when using join. |
247
|
|
|
* |
248
|
|
|
* @param string $name |
249
|
|
|
* @return \Closure |
250
|
|
|
* @throws \InvalidArgumentException |
251
|
|
|
*/ |
252
|
2 |
View Code Duplication |
public function filtering($name) |
|
|
|
|
253
|
|
|
{ |
254
|
|
|
// note, once we stop supporting php 5.5, we can rewrite the code below |
255
|
|
|
// to the reducing($name, ...$args) structure. |
|
|
|
|
256
|
|
|
// http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list |
257
|
|
|
|
258
|
2 |
|
if (is_string($name) && in_array($name, [ |
259
|
2 |
|
'equals', |
260
|
2 |
|
'in', |
261
|
2 |
|
'match', |
262
|
2 |
|
'not', |
263
|
2 |
|
'not_in', |
264
|
2 |
|
'type', |
265
|
2 |
|
])) { |
266
|
1 |
|
return call_user_func_array(sprintf('\Zicht\Itertools\filters\%s', $name), array_slice(func_get_args(), 1)); |
267
|
|
|
} |
268
|
|
|
|
269
|
1 |
|
throw new \InvalidArgumentException(sprintf('$NAME "%s" is not a valid filter.', $name)); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Make an iterator that returns values from $iterable where the |
274
|
|
|
* $strategy determines that the values are not empty. |
275
|
|
|
* |
276
|
|
|
* @param array|string|\Iterator $iterable |
277
|
|
|
* @param string|\Closure $strategy |
278
|
|
|
* @return Itertools\lib\FilterIterator |
279
|
|
|
* |
280
|
|
|
* @deprecated Use filter instead! |
281
|
|
|
*/ |
282
|
|
|
public function deprecatedFilterBy($iterable, $strategy) |
283
|
|
|
{ |
284
|
|
|
return Itertools\filter($strategy, $iterable); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Make an iterator that returns consecutive groups from the |
290
|
|
|
* $iterable. Generally, the $iterable needs to already be sorted on |
291
|
|
|
* the same key function. |
292
|
|
|
* |
293
|
|
|
* @param array|string|\Iterator $iterable |
294
|
|
|
* @param string|\Closure $strategy |
295
|
|
|
* @return Itertools\lib\GroupbyIterator |
296
|
|
|
* |
297
|
|
|
* @deprecated Use group_by instead! |
298
|
|
|
*/ |
299
|
|
|
public function deprecatedGroupBy($iterable, $strategy) |
300
|
|
|
{ |
301
|
|
|
return Itertools\group_by($strategy, $iterable); |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* Make an iterator returning values from $iterable and keys from |
306
|
|
|
* $strategy. |
307
|
|
|
* |
308
|
|
|
* @param array|string|\Iterator $iterable |
309
|
|
|
* @param string|\Closure $strategy |
310
|
|
|
* @return Itertools\lib\MapByIterator |
311
|
|
|
* |
312
|
|
|
* @deprecated Use map_by instead! |
313
|
|
|
*/ |
314
|
|
|
public function deprecatedMapBy($iterable, $strategy) |
315
|
|
|
{ |
316
|
|
|
return Itertools\mapBy($strategy, $iterable); |
|
|
|
|
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Create a reduction |
321
|
|
|
* |
322
|
|
|
* @param array|string|\Iterator $iterable |
323
|
|
|
* @param int $default |
324
|
|
|
* @return int |
325
|
|
|
* |
326
|
|
|
* @deprecated Use reduce instead! |
327
|
|
|
*/ |
328
|
|
|
public function deprecatedSum($iterable, $default = 0) |
329
|
|
|
{ |
330
|
|
|
$result = $default; |
331
|
|
|
foreach (Itertools\accumulate($iterable) as $result) { |
|
|
|
|
332
|
|
|
}; |
333
|
|
|
return $result; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Takes an iterable and returns another iterable that is unique. |
338
|
|
|
* |
339
|
|
|
* @param array|string|\Iterator $iterable |
340
|
|
|
* @param mixed $strategy |
341
|
|
|
* @return Itertools\lib\UniqueIterator |
342
|
|
|
* |
343
|
|
|
* @deprecated Use unique instead! |
344
|
|
|
*/ |
345
|
|
|
public function deprecatedUniqueBy($iterable, $strategy = null) |
346
|
|
|
{ |
347
|
|
|
return Itertools\unique($strategy, $iterable); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Returns a reduction closure |
352
|
|
|
* |
353
|
|
|
* @param string $name |
354
|
|
|
* @return \Closure |
355
|
|
|
* @throws \InvalidArgumentException |
356
|
|
|
* |
357
|
|
|
* @deprecated Use reducing instead! |
358
|
|
|
*/ |
359
|
|
|
public function deprecatedGetReduction($name) |
|
|
|
|
360
|
|
|
{ |
361
|
|
|
return call_user_func_array([$this, 'reducing'], func_get_args()); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* @{inheritDoc} |
366
|
|
|
*/ |
367
|
1 |
|
public function getName() |
368
|
|
|
{ |
369
|
1 |
|
return 'zicht_itertools'; |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
|
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.