Completed
Push — master ( 163cb3...f3c4fb )
by Sebastian
02:57
created

PasswordGenerator   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 17
eloc 42
dl 0
loc 163
ccs 41
cts 41
cp 1
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getTopology() 0 10 2
A getFromRandom() 0 11 2
A getRandomChar() 0 10 3
A inRanges() 0 9 4
A getTopologyGroup() 0 12 3
A getFromTopology() 0 19 3
1
<?php
2
3
/**
4
 * Linna Framework.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Authentication;
13
14
use InvalidArgumentException;
15
16
/**
17
 * Generate random password with random topology, this class use php7
18
 * random_int() function for generate random numbers.
19
 */
20
class PasswordGenerator
21
{
22
    /**
23
     * @var array Characters intervall utf8 dec rappresentation
24
     */
25
    private $chars = [
26
        [[65, 90]], //u 117
27
        [[97, 122]], //l 108
28
        [[48, 57]], //d 100
29
        [[33, 47], [58, 64], [91, 96], [123, 126]], //s 115
30
    ];
31
32
    /**
33
     * Generate a random password.
34
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
35
     *
36
     * $passwordGenerator = new PasswordGenerator();
37
     * $random = $passwordGenerator->getFromRandom(20);
38
     *
39
     * //var_dump result
40
     * //r4Q,1J*tM7D_99q0u>61
41
     * var_dump($random);
42
     * </code></pre>
43
     *
44
     * @param int $length Desiderated password length.
45
     * @return string Random password.
46
     */
47 8
    public function getFromRandom(int $length): string
48
    {
49 8
        $password = [];
50
51 8
        while ($length) {
52 8
            $password[] = $this->getRandomChar($this->chars[random_int(0, 3)]);
53
54 8
            $length--;
55
        }
56
57 8
        return implode($password);
58
    }
59
60
    /**
61
     * Return topology for given password.
62
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
63
     *
64
     * $passwordGenerator = new PasswordGenerator();
65
     * $topology = $passwordGenerator->getTopology('r4Q,1J*tM7D_99q0u>61');
66
     *
67
     * //var_dump result
68
     * //ldusdusludusddldlsdd
69
     * var_dump($topology);
70
     * </code></pre>
71
     * @param string $password Password.
72
     *
73
     * @return string Topology for the argument passed password.
74
     */
75 26
    public function getTopology(string $password): string
76
    {
77 26
        $array = (array) str_split($password);
78 26
        $topology = [];
79
80 26
        foreach ($array as $char) {
81 26
            $topology[] = $this->getTopologyGroup((string)$char);
82
        }
83
84 25
        return implode($topology);
85
    }
86
87
    /**
88
     * Return topology group for the given char.
89
     *
90
     * @param string $char
91
     *
92
     * @return string
93
     *
94
     * @throws InvalidArgumentException If char provided isn't inside any group
95
     */
96 26
    private function getTopologyGroup(string $char): string
97
    {
98 26
        $int = ord($char);
99 26
        $groups = ['u', 'l', 'd', 's'];
100
101 26
        foreach ($groups as $key => $group) {
102 26
            if ($this->inRanges($int, $this->chars[$key])) {
103 26
                return $group;
104
            }
105
        }
106
107 1
        throw new InvalidArgumentException('Out of group character provided');
108
    }
109
110
    /**
111
     * Generate a random password corresponding at the given topology.
112
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
113
     *
114
     * $passwordGenerator = new PasswordGenerator();
115
     * $random = $passwordGenerator->getFromTopology('ldusdusludusddldlsdd');
116
     *
117
     * //var_dump result
118
     * //r4Q,1J*tM7D_99q0u>61
119
     * var_dump($random);
120
     * </code></pre>
121
     *
122
     * @param string $topology Topology for generate password.
123
     * @return string Random password corresponding the given topology.
124
     *
125
     * @throws InvalidArgumentException If invalid pattern is provided.
126
     */
127 16
    public function getFromTopology(string $topology): string
128
    {
129 16
        $array = (array) str_split(strtolower($topology));
130 16
        $groups = [117 => 0, 108 => 1, 100 => 2, 115 => 3];
131 16
        $password = [];
132
133 16
        foreach ($array as $char) {
134 16
            $int = ord((string)$char);
135
136 16
            if (isset($groups[$int])) {
137 13
                $password[] = $this->getRandomChar($this->chars[$groups[$int]]);
138
139 13
                continue;
140
            }
141
142 6
            throw new InvalidArgumentException('Invalid pattern provided, accepted only u, l, d and s');
143
        }
144
145 10
        return implode($password);
146
    }
147
148
    /**
149
     * Get random char between.
150
     *
151
     * @param array $interval
152
     * @return string
153
     */
154 21
    private function getRandomChar(array $interval): string
155
    {
156
        do {
157 21
            $int = random_int(33, 122);
158 21
            if ($this->inRanges($int, $interval)) {
159 21
                break;
160
            }
161 21
        } while (true);
162
163 21
        return chr($int);
164
    }
165
166
    /**
167
     * Check if value is between given range.
168
     *
169
     * @param mixed $value
170
     * @param array $ranges
171
     *
172
     * @return bool
173
     */
174 33
    private function inRanges($value, array $ranges): bool
175
    {
176 33
        foreach ($ranges as $range) {
177 33
            if ($value >= $range[0] && $value <= $range[1]) {
178 33
                return true;
179
            }
180
        }
181
182 33
        return false;
183
    }
184
}
185