Completed
Branch master (25a17b)
by Karsten
02:55
created

Psi   D

Complexity

Total Complexity 34

Size/Duplication

Total Lines 402
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 34

Test Coverage

Coverage 82.98%

Importance

Changes 14
Bugs 0 Features 10
Metric Value
wmc 34
c 14
b 0
f 10
lcom 1
cbo 34
dl 0
loc 402
ccs 78
cts 94
cp 0.8298
rs 4.6

32 Methods

Rating   Name   Duplication   Size   Complexity  
A it() 0 4 1
A collect() 0 4 1
A toArray() 0 4 1
A join() 0 4 1
A getFirst() 0 4 1
A min() 0 4 1
A max() 0 4 1
A sum() 0 4 1
A avg() 0 4 1
A count() 0 4 1
A __construct() 0 8 1
A useFactory() 0 6 1
A preserveKeysOfMultipleInputs() 0 6 1
A filter() 0 6 1
A filterKey() 0 6 1
A filterValueKey() 0 6 1
A anyMatch() 0 6 1
A each() 0 6 1
A map() 0 6 1
A flatten() 0 6 1
A group() 0 6 1
A sort() 0 6 1
A sortBy() 0 6 1
A rsort() 0 6 1
A ksort() 0 6 1
A krsort() 0 6 1
A usort() 0 6 1
A unique() 0 6 1
A uksort() 0 6 1
A reverse() 0 6 1
A toMap() 0 9 2
A solveOperationsAndApplyTerminal() 0 15 2
1
<?php
2
/**
3
 * File was created 06.05.2015 16:09
4
 *
5
 * @author Karsten J. Gerber <[email protected]>
6
 */
7
namespace PeekAndPoke\Component\Psi;
8
9
use PeekAndPoke\Component\Psi\Exception\PsiException;
10
use PeekAndPoke\Component\Psi\Functions\Unary\Mapper\IdentityMapper;
11
use PeekAndPoke\Component\Psi\Interfaces\Functions\BinaryFunctionInterface;
12
use PeekAndPoke\Component\Psi\Interfaces\Functions\UnaryFunctionInterface;
13
use PeekAndPoke\Component\Psi\Interfaces\Operation\TerminalOperationInterface;
14
use PeekAndPoke\Component\Psi\Interfaces\PsiFactoryInterface;
15
use PeekAndPoke\Component\Psi\Operation\FullSet\FlattenOperation;
16
use PeekAndPoke\Component\Psi\Operation\FullSet\GroupOperation;
17
use PeekAndPoke\Component\Psi\Operation\FullSet\KeyReverseSortOperation;
18
use PeekAndPoke\Component\Psi\Operation\FullSet\KeySortOperation;
19
use PeekAndPoke\Component\Psi\Operation\FullSet\ReverseOperation;
20
use PeekAndPoke\Component\Psi\Operation\FullSet\ReverseSortOperation;
21
use PeekAndPoke\Component\Psi\Operation\FullSet\SortByOperation;
22
use PeekAndPoke\Component\Psi\Operation\FullSet\SortOperation;
23
use PeekAndPoke\Component\Psi\Operation\FullSet\UniqueOperation;
24
use PeekAndPoke\Component\Psi\Operation\FullSet\UserKeySortOperation;
25
use PeekAndPoke\Component\Psi\Operation\FullSet\UserSortOperation;
26
use PeekAndPoke\Component\Psi\Operation\Intermediate\EmptyIntermediateOp;
27
use PeekAndPoke\Component\Psi\Operation\Intermediate\Functional\EachOperation;
28
use PeekAndPoke\Component\Psi\Operation\Intermediate\Functional\MapOperation;
29
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\AnyMatchPredicate;
30
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterKeyPredicate;
31
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterPredicate;
32
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterValueKeyPredicate;
33
use PeekAndPoke\Component\Psi\Operation\Terminal\AverageOperation;
34
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectOperation;
35
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectToArrayOperation;
36
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectToMapOperation;
37
use PeekAndPoke\Component\Psi\Operation\Terminal\CountOperation;
38
use PeekAndPoke\Component\Psi\Operation\Terminal\GetFirstOperation;
39
use PeekAndPoke\Component\Psi\Operation\Terminal\JoinOperation;
40
use PeekAndPoke\Component\Psi\Operation\Terminal\MaxOperation;
41
use PeekAndPoke\Component\Psi\Operation\Terminal\MinOperation;
42
use PeekAndPoke\Component\Psi\Operation\Terminal\SumOperation;
43
44
/**
45
 * Psi the Php Streams Api Implementation (inspired by Java Streams Api)
46
 *
47
 * @author Karsten J. Gerber <[email protected]>
48
 */
49
class Psi
50
{
51
    /** @var array */
52
    private $inputs;
53
54
    /** @var \ArrayIterator */
55
    private $operationChain = null;
56
    /** @var PsiFactoryInterface */
57
    private $factory = null;
58
59
    private $options = null;
60
61
    /**
62
     * @param mixed $_ Everything that can be iterated, Provide as many params as you want (from 1 to n)
63
     *
64
     * @return Psi
65
     * @throws PsiException
66
     */
67 70
    public static function it($_ = null)
68
    {
69 70
        return new Psi(func_get_args());
70
    }
71
72
    /**
73
     * @param array $inputs
74
     */
75 70
    protected function __construct($inputs)
76
    {
77 70
        $this->operationChain = new \ArrayIterator();
78 70
        $this->factory        = new PsiFactory();
79 70
        $this->options        = new PsiOptions();
80
81 70
        $this->inputs = $inputs;
82 70
    }
83
84
    ////  CONFIGURATION METHODS  ///////////////////////////////////////////////////////////////////////////////////////
85
86
    /**
87
     * @param PsiFactoryInterface $factory
88
     *
89
     * @return $this
90
     */
91
    public function useFactory(PsiFactoryInterface $factory)
92
    {
93
        $this->factory = $factory;
94
95
        return $this;
96
    }
97
98
    /**
99
     * Configure if the keys should be preserved when we have multiple inputs.
100
     *
101
     * Depending on the inputs, preserving the keys will most likely lead to conflicts in the operation like
102
     * - sort
103
     * - unique
104
     * Thus the outcome of these operations will be unpredicted.
105
     *
106
     * By default this option is turned OFF. So for multiple inputs the keys will get lost. On the other hand all
107
     * values will survive all operations.
108
     *
109
     * @param bool $preserve
110
     *
111
     * @return $this
112
     */
113
    public function preserveKeysOfMultipleInputs($preserve = true)
114
    {
115
        $this->options->setPreserveKeysOfMultipleInputs($preserve);
116
117
        return $this;
118
    }
119
120
    ////  INTERMEDIATE OPERATIONS - working on one item at a time  /////////////////////////////////////////////////////
121
122
    /**
123
     * @param \Closure|UnaryFunctionInterface $unaryFunction
124
     *
125
     * @return $this
126
     */
127 8
    public function filter($unaryFunction)
128
    {
129 8
        $this->operationChain->append(new FilterPredicate($unaryFunction));
130
131 8
        return $this;
132
    }
133
134
    /**
135
     * @param \Closure|UnaryFunctionInterface $unaryFunction
136
     *
137
     * @return $this
138
     */
139 5
    public function filterKey($unaryFunction)
140
    {
141 5
        $this->operationChain->append(new FilterKeyPredicate($unaryFunction));
142
143 5
        return $this;
144
    }
145
146
    /**
147
     * @param \Closure|BinaryFunctionInterface $biPredicate
148
     *
149
     * @return $this
150
     */
151 5
    public function filterValueKey($biPredicate)
152
    {
153 5
        $this->operationChain->append(new FilterValueKeyPredicate($biPredicate));
154
155 5
        return $this;
156
    }
157
158
    /**
159
     * @param \Closure|UnaryFunctionInterface $unaryFunction
160
     *
161
     * @return $this
162
     */
163 7
    public function anyMatch($unaryFunction)
164
    {
165 7
        $this->operationChain->append(new AnyMatchPredicate($unaryFunction));
166
167 7
        return $this;
168
    }
169
170
    /**
171
     * @param \Closure|BinaryFunctionInterface $biFunction
172
     *
173
     * @return $this
174
     */
175 4
    public function each($biFunction)
176
    {
177 4
        $this->operationChain->append(new EachOperation($biFunction));
178
179 4
        return $this;
180
    }
181
182
    /**
183
     * @param \Closure|BinaryFunctionInterface $function
184
     *
185
     * @return $this
186
     */
187 12
    public function map($function)
188
    {
189 12
        $this->operationChain->append(new MapOperation($function));
190
191 12
        return $this;
192
    }
193
194
    ////  FULL SET OPERATIONS - working on all items at a time  ////////////////////////////////////////////////////////
195
196
    /**
197
     * Flattens the input recursively into a no longer nested iterator.
198
     *
199
     * Example: [1, [2, [3, 4], 5], 6] will become [1, 2, 3, 4, 5, 6]
200
     *
201
     * @return $this
202
     */
203 4
    public function flatten()
204
    {
205 4
        $this->operationChain->append(new FlattenOperation());
206
207 4
        return $this;
208
    }
209
210
    /**
211
     * @param UnaryFunctionInterface|\Closure $function
212
     *
213
     * @return $this
214
     */
215
    public function group($function)
216
    {
217
        $this->operationChain->append(new GroupOperation($function));
218
219
        return $this;
220
    }
221
222
    /**
223
     * @param int|null $sortFlags
224
     *
225
     * @see sort()
226
     *
227
     * @return $this
228
     */
229 3
    public function sort($sortFlags = null)
230
    {
231 3
        $this->operationChain->append(new SortOperation($sortFlags));
232
233 3
        return $this;
234
    }
235
236
    /**
237
     * @param UnaryFunctionInterface|\Closure $function Return the value used for comparison
238
     *
239
     * @return $this
240
     */
241
    public function sortBy($function)
242
    {
243
        $this->operationChain->append(new SortByOperation($function));
244
245
        return $this;
246
    }
247
248
    /**
249
     * @param int|null $sortFlags
250
     *
251
     * @see sort()
252
     *
253
     * @return $this
254
     */
255 1
    public function rsort($sortFlags = null)
256
    {
257 1
        $this->operationChain->append(new ReverseSortOperation($sortFlags));
258
259 1
        return $this;
260
    }
261
262
    /**
263
     * @param int|null $sortFlags
264
     *
265
     * @see ksort()
266
     *
267
     * @return $this
268
     */
269 1
    public function ksort($sortFlags = null)
270
    {
271 1
        $this->operationChain->append(new KeySortOperation($sortFlags));
272
273 1
        return $this;
274
    }
275
276
    /**
277
     * @param int|null $sortFlags
278
     *
279
     * @return $this
280
     */
281 1
    public function krsort($sortFlags = null)
282
    {
283 1
        $this->operationChain->append(new KeyReverseSortOperation($sortFlags));
284
285 1
        return $this;
286
    }
287
288
    /**
289
     * @param BinaryFunctionInterface|\Closure $biFunction
290
     *
291
     * @return $this
292
     */
293 2
    public function usort($biFunction)
294
    {
295 2
        $this->operationChain->append(new UserSortOperation($biFunction));
296
297 2
        return $this;
298
    }
299
300
    /**
301
     * @param int|null $sortFlags
302
     *
303
     * @return $this
304
     */
305 1
    public function unique($sortFlags = null)
306
    {
307 1
        $this->operationChain->append(new UniqueOperation($sortFlags));
308
309 1
        return $this;
310
    }
311
312
    /**
313
     * @param BinaryFunctionInterface|\Closure $biFunction
314
     *
315
     * @return $this
316
     */
317 1
    public function uksort($biFunction)
318
    {
319 1
        $this->operationChain->append(new UserKeySortOperation($biFunction));
320
321 1
        return $this;
322
    }
323
324
    /**
325
     * @return $this
326
     */
327 1
    public function reverse()
328
    {
329 1
        $this->operationChain->append(new ReverseOperation());
330
331 1
        return $this;
332
    }
333
334
    ////  TERMINAL OPERATIONS - generating output and ending the chain  ////////////////////////////////////////////////
335
336
    /**
337
     * @return \Iterator
338
     */
339 33
    public function collect()
340
    {
341 33
        return $this->solveOperationsAndApplyTerminal(new CollectOperation());
342
    }
343
344
    /**
345
     * @return array
346
     */
347 27
    public function toArray()
348
    {
349 27
        return $this->solveOperationsAndApplyTerminal(new CollectToArrayOperation());
350
    }
351
352
    /**
353
     * @param BinaryFunctionInterface|Callable $keyMapper   Maps the value down to a string or number
354
     * @param BinaryFunctionInterface|Callable $valueMapper If not given the value will not be changed
355
     *
356
     * @return array
357
     */
358
    public function toMap($keyMapper, $valueMapper = null)
359
    {
360
        return $this->solveOperationsAndApplyTerminal(
361
            new CollectToMapOperation(
362
                $keyMapper,
363
                $valueMapper ? $valueMapper : new IdentityMapper()
364
            )
365
        );
366
    }
367
368
    /**
369
     * @param string $delimiter
370
     *
371
     * @return string
372
     */
373 1
    public function join($delimiter)
374
    {
375 1
        return $this->solveOperationsAndApplyTerminal(new JoinOperation($delimiter));
376
    }
377
378
    /**
379
     * @param mixed $default
380
     *
381
     * @return mixed
382
     */
383 2
    public function getFirst($default = null)
384
    {
385 2
        return $this->solveOperationsAndApplyTerminal(new GetFirstOperation($default));
386
    }
387
388
    /**
389
     * @return float
390
     */
391 1
    public function min()
392
    {
393 1
        return $this->solveOperationsAndApplyTerminal(new MinOperation());
394
    }
395
396
    /**
397
     * @return float
398
     */
399 1
    public function max()
400
    {
401 1
        return $this->solveOperationsAndApplyTerminal(new MaxOperation());
402
    }
403
404
    /**
405
     * @return float
406
     */
407 2
    public function sum()
408
    {
409 2
        return $this->solveOperationsAndApplyTerminal(new SumOperation());
410
    }
411
412
    /**
413
     * @return float
414
     */
415 2
    public function avg()
416
    {
417 2
        return $this->solveOperationsAndApplyTerminal(new AverageOperation());
418
    }
419
420
    /**
421
     * @return int
422
     */
423 1
    public function count()
424
    {
425 1
        return $this->solveOperationsAndApplyTerminal(new CountOperation());
426
    }
427
428
    ////  PRIVATE METHODS  /////////////////////////////////////////////////////////////////////////////////////////////
429
430
    /**
431
     * @param TerminalOperationInterface $terminal
432
     *
433
     * @return mixed
434
     */
435 70
    private function solveOperationsAndApplyTerminal(TerminalOperationInterface $terminal)
436
    {
437
        // When we have not a single operation in the chain we add a dummy one, in order to map down multiple input
438
        // living inside the \AppendIterator (in case we have multiple inputs)
439 70
        if (count($this->operationChain) == 0) {
440 22
            $this->operationChain->append(new EmptyIntermediateOp());
441
        }
442
443 70
        $iterator = $this->factory->createIterator($this->inputs, $this->options);
444 70
        $solver   = $this->factory->createSolver();
445
446 70
        $result = $solver->solve($this->operationChain, $iterator);
447
448 70
        return $terminal->apply($result);
449
    }
450
}
451