Completed
Push — development ( 1fb984...7ea9e3 )
by Nils
08:29
created

RequirementPasswordGenerator::validLimits()   D

Complexity

Conditions 10
Paths 128

Size

Total Lines 46
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 22
nc 128
nop 0
dl 0
loc 46
rs 4.75
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
namespace PasswordGenerator\Generator;
4
5
use PasswordGenerator\Exception\ImpossibleMinMaxLimitsException;
6
use PasswordGenerator\Exception\InvalidOptionException;
7
8
/**
9
 * Class RequirementPasswordGenerator
10
 *
11
 * Works just like ComputerPasswordGenerator with the addition of minimum and maximum counts.
12
 *
13
 * @package Hackzilla\PasswordGenerator\Generator
14
 */
15
class RequirementPasswordGenerator extends ComputerPasswordGenerator
16
{
17
    private $minimumCounts = array();
18
    private $maximumCounts = array();
19
    private $validOptions = array();
20
    private $dirtyCheck = true;
21
22
    /**
23
     */
24
    public function __construct()
25
    {
26
        parent::__construct();
27
28
        $this->validOptions = array(
29
            self::OPTION_UPPER_CASE,
30
            self::OPTION_LOWER_CASE,
31
            self::OPTION_NUMBERS,
32
            self::OPTION_SYMBOLS,
33
        );
34
    }
35
36
    /**
37
     * Generate one password based on options.
38
     *
39
     * @return string password
40
     * @throws ImpossibleMinMaxLimitsException
41
     * @throws \Hackzilla\PasswordGenerator\Exception\CharactersNotFoundException
42
     */
43
    public function generatePassword()
44
    {
45
        if ($this->dirtyCheck) {
46
            if (!$this->validLimits()) {
47
                throw new ImpossibleMinMaxLimitsException();
48
            }
49
50
            $this->dirtyCheck = false;
51
        }
52
53
        do {
54
            $password = parent::generatePassword();
55
        } while (!$this->validatePassword($password));
56
57
        return $password;
58
    }
59
60
    /**
61
     * Password minimum count for option.
62
     *
63
     * @param string $option Use class constants
64
     *
65
     * @return int|null
66
     */
67
    public function getMinimumCount($option)
68
    {
69
        return isset($this->minimumCounts[$option]) ? $this->minimumCounts[$option] : null;
70
    }
71
72
    /**
73
     * Password maximum count for option.
74
     *
75
     * @param string $option Use class constants
76
     *
77
     * @return int|null
78
     */
79
    public function getMaximumCount($option)
80
    {
81
        return isset($this->maximumCounts[$option]) ? $this->maximumCounts[$option] : null;
82
    }
83
84
    /**
85
     * Set minimum count of option for desired password(s).
86
     *
87
     * @param string   $option Use class constants
88
     * @param int|null $characterCount
89
     *
90
     * @return $this
91
     *
92
     * @throws InvalidOptionException
93
     */
94 View Code Duplication
    public function setMinimumCount($option, $characterCount)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
95
    {
96
        $this->dirtyCheck = true;
97
98
        if (!$this->validOption($option)) {
99
            throw new InvalidOptionException('Invalid Option');
100
        }
101
102
        if (is_null($characterCount)) {
103
            unset($this->minimumCounts[$option]);
104
105
            return $this;
106
        }
107
108
        if (!is_int($characterCount) || $characterCount < 0) {
109
            throw new \InvalidArgumentException('Expected non-negative integer');
110
        }
111
112
        $this->minimumCounts[$option] = $characterCount;
113
114
        return $this;
115
    }
116
117
    /**
118
     * Set maximum count of option for desired password(s).
119
     *
120
     * @param string   $option Use class constants
121
     * @param int|null $characterCount
122
     *
123
     * @return $this
124
     *
125
     * @throws InvalidOptionException
126
     */
127 View Code Duplication
    public function setMaximumCount($option, $characterCount)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
128
    {
129
        $this->dirtyCheck = true;
130
131
        if (!$this->validOption($option)) {
132
            throw new InvalidOptionException('Invalid Option');
133
        }
134
135
        if (is_null($characterCount)) {
136
            unset($this->maximumCounts[$option]);
137
138
            return $this;
139
        }
140
141
        if (!is_int($characterCount) || $characterCount < 0) {
142
            throw new \InvalidArgumentException('Expected non-negative integer');
143
        }
144
145
        $this->maximumCounts[$option] = $characterCount;
146
147
        return $this;
148
    }
149
150
    public function validLimits()
151
    {
152
        $elements = 0;
153
154
        if ($this->getOptionValue(self::OPTION_UPPER_CASE)) {
155
            $elements++;
156
        }
157
158
        if ($this->getOptionValue(self::OPTION_LOWER_CASE)) {
159
            $elements++;
160
        }
161
162
        if ($this->getOptionValue(self::OPTION_NUMBERS)) {
163
            $elements++;
164
        }
165
166
        if ($this->getOptionValue(self::OPTION_SYMBOLS)) {
167
            $elements++;
168
        }
169
170
        // check if there is wiggle room in minimums
171
        $total = 0;
172
173
        foreach ($this->minimumCounts as $minOption => $minCount) {
174
            $total += $minCount;
175
        }
176
177
        if ($total > $this->getLength()) {
178
            return false;
179
        }
180
181
        // check if there is wiggle room in maximums
182
        if ($elements <= count($this->maximumCounts)) {
183
            $total = 0;
184
185
            foreach ($this->maximumCounts as $maxOption => $maxCount) {
186
                $total += $maxCount;
187
            }
188
189
            if ($total < $this->getLength()) {
190
                return false;
191
            }
192
        }
193
194
        return true;
195
    }
196
197
    /**
198
     * @param string $option
199
     *
200
     * @return bool
201
     */
202
    public function validOption($option)
203
    {
204
        return in_array($option, $this->validOptions, true);
205
    }
206
207
    /**
208
     * Generate $count number of passwords.
209
     *
210
     * @param int $count Number of passwords to return
211
     *
212
     * @return array
213
     *
214
     * @throws \InvalidArgumentException
215
     */
216 View Code Duplication
    public function generatePasswords($count = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
217
    {
218
        if (!is_int($count)) {
219
            throw new \InvalidArgumentException('Expected integer');
220
        } elseif ($count < 0) {
221
            throw new \InvalidArgumentException('Expected positive integer');
222
        }
223
224
        $passwords = array();
225
226
        for ($i = 0; $i < $count; $i++) {
227
            $passwords[] = $this->generatePassword();
228
        }
229
230
        return $passwords;
231
    }
232
233
    /**
234
     * Check password is valid when comparing to minimum and maximum counts of options.
235
     *
236
     * @param string $password
237
     *
238
     * @return bool
239
     */
240
    public function validatePassword($password)
241
    {
242
        foreach ($this->validOptions as $option) {
243
            $minCount = $this->getMinimumCount($option);
244
            $maxCount = $this->getMaximumCount($option);
245
            $count = strlen(preg_replace('|[^'.preg_quote($this->getParameter($option)).']|', '', $password));
246
247
            if (!is_null($minCount) && $count < $minCount) {
248
                return false;
249
            }
250
251
            if (!is_null($maxCount) && $count > $maxCount) {
252
                return false;
253
            }
254
        }
255
256
        return true;
257
    }
258
}
259