Passed
Branch master (1d5b30)
by John
02:18
created

Generator::generate()   F

Complexity

Conditions 14
Paths 513

Size

Total Lines 55
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 24
nc 513
nop 4
dl 0
loc 55
rs 2.7763
c 0
b 0
f 0

How to fix   Long Method    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 PasswordHelper;
6
7
/**
8
 * Generates secure random passwords based on specified requirements.
9
 *
10
 * This class provides methods to generate passwords with various character types
11
 * and ensures they meet minimum security requirements.
12
 *
13
 * @package PasswordHelper
14
 */
15
class Generator
16
{
17
    /**
18
     * Default minimum length for generated passwords.
19
     */
20
    private const DEFAULT_MIN_LENGTH = 12;
21
22
    /**
23
     * Default maximum length for generated passwords.
24
     */
25
    private const DEFAULT_MAX_LENGTH = 20;
26
27
    /**
28
     * Creates a new password generator.
29
     */
30
    public function __construct(
31
        private int $minLength = self::DEFAULT_MIN_LENGTH,
32
        private int $maxLength = self::DEFAULT_MAX_LENGTH
33
    ) {
34
        if ($this->minLength < 8) {
35
            throw new \InvalidArgumentException('Minimum length must be at least 8 characters');
36
        }
37
38
        if ($this->maxLength < $this->minLength) {
39
            throw new \InvalidArgumentException('Maximum length must be greater than minimum length');
40
        }
41
    }
42
43
    /**
44
     * Gets the minimum length for generated passwords.
45
     *
46
     * @return int The minimum length
47
     */
48
    public function getMinLength(): int
49
    {
50
        return $this->minLength;
51
    }
52
53
    /**
54
     * Gets the maximum length for generated passwords.
55
     *
56
     * @return int The maximum length
57
     */
58
    public function getMaxLength(): int
59
    {
60
        return $this->maxLength;
61
    }
62
63
    /**
64
     * Generates a random password that meets the specified requirements.
65
     *
66
     * @param bool $includeUppercase Whether to include uppercase letters
67
     * @param bool $includeLowercase Whether to include lowercase letters
68
     * @param bool $includeNumbers Whether to include numbers
69
     * @param bool $includeSpecial Whether to include special characters
70
     * @return string The generated password
71
     * @throws \InvalidArgumentException If no character types are selected
72
     */
73
    public function generate(
74
        bool $includeUppercase = true,
75
        bool $includeLowercase = true,
76
        bool $includeNumbers = true,
77
        bool $includeSpecial = true
78
    ): string {
79
        if (!$includeUppercase && !$includeLowercase && !$includeNumbers && !$includeSpecial) {
80
            throw new \InvalidArgumentException('At least one character type must be selected');
81
        }
82
83
        $chars = [];
84
85
        if ($includeUppercase) {
86
            $chars = array_merge($chars, range('A', 'Z'));
87
        }
88
89
        if ($includeLowercase) {
90
            $chars = array_merge($chars, range('a', 'z'));
91
        }
92
93
        if ($includeNumbers) {
94
            $chars = array_merge($chars, array_map('strval', range(0, 9)));
95
        }
96
97
        if ($includeSpecial) {
98
            $chars = array_merge($chars, str_split('!@#$%^&*()_+-=[]{}|;:,.<>?'));
0 ignored issues
show
Bug introduced by
It seems like str_split('!@#$%^&*()_+-=[]{}|;:,.<>?') can also be of type true; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
            $chars = array_merge($chars, /** @scrutinizer ignore-type */ str_split('!@#$%^&*()_+-=[]{}|;:,.<>?'));
Loading history...
99
        }
100
101
        $length = random_int($this->minLength, $this->maxLength);
102
        $password = '';
103
104
        // Ensure at least one character from each selected type
105
        if ($includeUppercase) {
106
            $password .= $this->getRandomCharacter(range('A', 'Z'));
107
        }
108
109
        if ($includeLowercase) {
110
            $password .= $this->getRandomCharacter(range('a', 'z'));
111
        }
112
113
        if ($includeNumbers) {
114
            $password .= $this->getRandomCharacter(array_map('strval', range(0, 9)));
115
        }
116
117
        if ($includeSpecial) {
118
            $password .= $this->getRandomCharacter(str_split('!@#$%^&*()_+-=[]{}|;:,.<>?'));
0 ignored issues
show
Bug introduced by
It seems like str_split('!@#$%^&*()_+-=[]{}|;:,.<>?') can also be of type true; however, parameter $chars of PasswordHelper\Generator::getRandomCharacter() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
            $password .= $this->getRandomCharacter(/** @scrutinizer ignore-type */ str_split('!@#$%^&*()_+-=[]{}|;:,.<>?'));
Loading history...
119
        }
120
121
        // Fill the rest of the password with random characters
122
        while (strlen($password) < $length) {
123
            $password .= $this->getRandomCharacter($chars);
124
        }
125
126
        // Shuffle the password to ensure random distribution
127
        return str_shuffle($password);
128
    }
129
130
    /**
131
     * Gets a random character from the given array.
132
     *
133
     * @param array<int, string> $chars Array of characters to choose from
134
     * @return string A random character
135
     */
136
    private function getRandomCharacter(array $chars): string
137
    {
138
        return (string) $chars[random_int(0, count($chars) - 1)];
139
    }
140
}
141