SubstringGenerator::generateNotStartingWithRoot()   B
last analyzed

Complexity

Conditions 10
Paths 7

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
eloc 15
nc 7
nop 3
dl 0
loc 27
ccs 16
cts 16
cp 1
crap 10
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\Formatter;
6
7
use InvalidArgumentException;
8
use Stadly\PasswordPolice\CharTree;
9
use Stadly\PasswordPolice\Formatter;
10
11
final class SubstringGenerator implements Formatter
12
{
13
    use Chaining;
14
15
    /**
16
     * @var int Minimum substring length.
17
     */
18
    private $minLength;
19
20
    /**
21
     * @var int|null Maximum substring length.
22
     */
23
    private $maxLength;
24
25
    /**
26
     * @var array<CharTree> Memoization of formatted character trees.
27
     */
28
    private static $memoization = [];
29
30
    /**
31
     * @var array<CharTree> Memoization of intermediate results of formatted character trees.
32
     */
33
    private static $startsWithMemoization = [];
34
35
    /**
36
     * @param int $minLength Ignore substrings shorter than this.
37
     * @param int|null $maxLength Ignore substrings longer than this.
38
     */
39 8
    public function __construct(int $minLength = 3, ?int $maxLength = 25)
40
    {
41 8
        if ($minLength < 0) {
42 1
            throw new InvalidArgumentException('Min length must be non-negative.');
43
        }
44 7
        if ($maxLength !== null && $maxLength < $minLength) {
45 1
            throw new InvalidArgumentException('Max length cannot be smaller than min length.');
46
        }
47
48 6
        $this->minLength = $minLength;
49 6
        $this->maxLength = $maxLength;
50 6
    }
51
52
    /**
53
     * @param CharTree $charTree Character tree to format.
54
     * @return CharTree Substrings of the character tree.
55
     */
56 6
    protected function applyCurrent(CharTree $charTree): CharTree
57
    {
58 6
        return $this->applyInternal($charTree, $this->minLength, $this->maxLength);
59
    }
60
61
    /**
62
     * @param CharTree $charTree Character tree to format.
63
     * @param int $minLength Minimum substring length.
64
     * @param int|null $maxLength Maximum substring length.
65
     * @return CharTree Substrings of the character tree.
66
     */
67 6
    private function applyInternal(CharTree $charTree, int $minLength, ?int $maxLength): CharTree
68
    {
69
        // When PHP 7.1 is no longer supported, change to using spl_object_id.
70 6
        $hash = spl_object_hash($charTree) . ';' . $minLength . ';' . $maxLength;
71
72 6
        if (!isset(self::$memoization[$hash])) {
73 6
            self::$memoization[$hash] = $this->format($charTree, $minLength, $maxLength);
74
        }
75
76 6
        return self::$memoization[$hash];
77
    }
78
79
    /**
80
     * @param CharTree $charTree Character tree to format.
81
     * @param int $minLength Minimum substring length.
82
     * @param int|null $maxLength Maximum substring length.
83
     * @return CharTree Substrings of the character tree. Memoization is not used.
84
     */
85 6
    private function format(CharTree $charTree, int $minLength, ?int $maxLength): CharTree
86
    {
87 6
        $charTrees = [];
88
89 6
        $notStartingWithRoot = $this->generateNotStartingWithRoot($charTree, $minLength, $maxLength);
90 6
        if ($notStartingWithRoot->getRoot() !== null) {
91 5
            $charTrees[] = $notStartingWithRoot;
92
        }
93
94 6
        $startingWithRoot = $this->applyInternalStartingWithRoot($charTree, $minLength, $maxLength);
95 6
        if ($startingWithRoot->getRoot() !== null) {
96 5
            $charTrees[] = $startingWithRoot;
97
        }
98
99 6
        return CharTree::fromArray($charTrees);
100
    }
101
102
    /**
103
     * @param CharTree $charTree Character tree to format.
104
     * @param int $minLength Minimum substring length.
105
     * @param int|null $maxLength Maximum substring length.
106
     * @return CharTree Substrings of the character tree not starting with root.
107
     */
108 6
    private function generateNotStartingWithRoot(CharTree $charTree, int $minLength, ?int $maxLength): CharTree
109
    {
110 6
        $root = $charTree->getRoot();
111
112 6
        if ($root === null) {
113 2
            return $charTree;
114
        }
115
116 5
        $branches = $charTree->getBranches();
117 5
        $substringBranches = [];
118 5
        if ($maxLength > 0 || $maxLength === null) {
119 5
            foreach ($branches as $branch) {
120 4
                $substringBranch = $this->applyInternal($branch, $minLength, $maxLength);
121 4
                if ($substringBranch->getRoot() !== null) {
122 4
                    $substringBranches[] = $substringBranch;
123
                }
124
            }
125
        }
126
127 5
        if ($substringBranches !== [] || $minLength === 0) {
128 5
            if ($substringBranches !== [] && $minLength === 0) {
129 1
                $substringBranches[] = CharTree::fromNothing();
130
            }
131 5
            return CharTree::fromString('', $substringBranches);
132
        }
133
134 3
        return CharTree::fromNothing();
135
    }
136
137
    /**
138
     * @param CharTree $charTree Character tree to format.
139
     * @param int $minLength Minimum substring length.
140
     * @param int|null $maxLength Maximum substring length.
141
     * @return CharTree Substrings of the character tree starting with root.
142
     */
143 6
    private function applyInternalStartingWithRoot(CharTree $charTree, int $minLength, ?int $maxLength): CharTree
144
    {
145
        // When PHP 7.1 is no longer supported, change to using spl_object_id.
146 6
        $hash = spl_object_hash($charTree) . ';' . $minLength . ';' . $maxLength;
147
148 6
        if (!isset(self::$startsWithMemoization[$hash])) {
149 6
            self::$startsWithMemoization[$hash] = $this->generateStartingWithRoot($charTree, $minLength, $maxLength);
150
        }
151
152 6
        return self::$startsWithMemoization[$hash];
153
    }
154
155
    /**
156
     * @param CharTree $charTree Character tree to format.
157
     * @param int $minLength Minimum substring length.
158
     * @param int|null $maxLength Maximum substring length.
159
     * @return CharTree Substrings of the character tree starting with root. Memoization is not used.
160
     */
161 6
    private function generateStartingWithRoot(CharTree $charTree, int $minLength, ?int $maxLength): CharTree
162
    {
163 6
        $root = $charTree->getRoot();
164
165 6
        if ($root === null) {
166 2
            return $charTree;
167
        }
168
169 5
        $rootLength = mb_strlen($root);
170 5
        if ($rootLength <= $maxLength || $maxLength === null) {
171 5
            $branches = $charTree->getBranches();
172 5
            $branchMinLength = $minLength <= $rootLength ? 0 : $minLength - $rootLength;
173 5
            $branchMaxLength = $maxLength === null ? null : $maxLength - $rootLength;
174
175 5
            $substringBranches = [];
176 5
            foreach ($branches as $branch) {
177 4
                $substringBranch = $this->applyInternalStartingWithRoot($branch, $branchMinLength, $branchMaxLength);
178 4
                if ($substringBranch->getRoot() !== null) {
179 4
                    $substringBranches[] = $substringBranch;
180
                }
181
            }
182
183 5
            if ($substringBranches !== [] || $minLength <= $rootLength) {
184 5
                if ($substringBranches !== [] && $minLength <= $rootLength) {
185 3
                    $substringBranches[] = CharTree::fromNothing();
186
                }
187 5
                return CharTree::fromString($root, $substringBranches);
188
            }
189
        }
190
191 3
        return CharTree::fromNothing();
192
    }
193
}
194