1<?php
2declare(strict_types = 1);
3namespace TYPO3\CMS\Core\Utility;
4
5/*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18/**
19 * Class with helper functions for permuting items.
20 */
21class PermutationUtility
22{
23    /**
24     * Combines string items of multiple arrays as cross-product into flat items.
25     *
26     * Example:
27     * + meltStringItems([['a', 'b'], ['c', 'd'], ['e', 'f']])
28     * + results into ['ace', 'acf', 'ade', 'adf', 'bce', 'bcf', 'bde', 'bdf']
29     *
30     * @param array[] $payload Distinct array that should be melted
31     * @param string $previousResult Previous item results
32     * @return array
33     */
34    public static function meltStringItems(array $payload, string $previousResult = ''): array
35    {
36        $results = [];
37        $items = static::nextItems($payload);
38        foreach ($items as $item) {
39            $resultItem = $previousResult . static::asString($item);
40            if (!empty($payload)) {
41                $results = array_merge(
42                    $results,
43                    static::meltStringItems($payload, $resultItem)
44                );
45                continue;
46            }
47            $results[] = $resultItem;
48        }
49        return $results;
50    }
51
52    /**
53     * Combines arbitrary items of multiple arrays as cross-product into flat items.
54     *
55     * Example:
56     * + meltArrayItems(['a','b'], ['c','e'], ['f','g'])
57     * + results into ['a', 'c', 'e'], ['a', 'c', 'f'], ['a', 'd', 'e'], ['a', 'd', 'f'],
58     *                ['b', 'c', 'e'], ['b', 'c', 'f'], ['b', 'd', 'e'], ['b', 'd', 'f'],
59     *
60     * @param array[] $payload Distinct items that should be melted
61     * @param array $previousResult Previous item results
62     * @return array
63     */
64    public static function meltArrayItems(array $payload, array $previousResult = []): array
65    {
66        $results = [];
67        $items = static::nextItems($payload);
68        foreach ($items as $item) {
69            $resultItems = $previousResult;
70            $resultItems[] = $item;
71            if (!empty($payload)) {
72                $results = array_merge(
73                    $results,
74                    static::meltArrayItems($payload, $resultItems)
75                );
76                continue;
77            }
78            $results[] = $resultItems;
79        }
80        return $results;
81    }
82
83    protected static function nextItems(array &$payload): iterable
84    {
85        $items = array_shift($payload);
86        if (is_iterable($items)) {
87            return $items;
88        }
89        throw new \LogicException(
90            sprintf('Expected iterable, got %s', gettype($items)),
91            1578164101
92        );
93    }
94
95    protected static function asString($item): string
96    {
97        if (is_string($item)) {
98            return $item;
99        }
100        if (is_object($item) && method_exists($item, '__toString')) {
101            return (string)$item;
102        }
103        throw new \LogicException(
104            sprintf('Expected string, got %s', gettype($item)),
105            1578164102
106        );
107    }
108}
109