Test Setup Failed
Branch master (977662)
by Sebastian
02:38
created

PasswordGenerator::getTopology()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
3
/**
4
 * Linna Framework.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2017, 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
     * @var array Characters utf8 dec rappresentation
34
     */
35
    private $groups = [
36
        117 => 0, //u
37
        108 => 1, //l
38
        100 => 2, //d
39
        115 => 3  //s
40
    ];
41
42
    
43
    /**
44
     * Generate a random password.
45
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
46
     *
47
     * $passwordGenerator = new PasswordGenerator();
48
     * $random = $passwordGenerator->getFromRandom(20);
49
     *
50
     * //var_dump result
51
     * //r4Q,1J*tM7D_99q0u>61
52
     * var_dump($random);
53
     * </code></pre>
54
     *
55
     * @param int $length Desiderated password length.
56
     * @return string Random password.
57
     */
58
    public function getFromRandom(int $length): string
59
    {
60
        $password = [];
61
62
        while ($length) {
63
            $password[] = $this->getRandomChar($this->chars[random_int(0, 3)]);
64
            
65
            $length--;
66
        }
67
        
68
        return implode($password);
69
    }
70
71
    /**
72
     * Return topology for given password.
73
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
74
     *
75
     * $passwordGenerator = new PasswordGenerator();
76
     * $topology = $passwordGenerator->getTopology('r4Q,1J*tM7D_99q0u>61');
77
     *
78
     * //var_dump result
79
     * //ldusdusludusddldlsdd
80
     * var_dump($topology);
81
     * </code></pre>
82
     * @param string $password Password.
83
     *
84
     * @return string Topology for the argument passed password.
85
     */
86
    public function getTopology(string $password): string
87
    {
88
        $array = str_split($password);
89
        $topology = [];
90
91
        foreach ($array as $char) {
92
            $topology[] = $this->getTopologyGroup($char);
93
        }
94
95
        return implode($topology);
96
    }
97
98
    /**
99
     * Return topology group.
100
     *
101
     * @param string $char
102
     *
103
     * @return string
104
     */
105
    private function getTopologyGroup(string $char) : string
106
    {
107
        $int = ord($char);
108
109
        if ($this->inRanges($int, $this->chars[0])) {
110
            return 'u';
111
        }
112
113
        if ($this->inRanges($int, $this->chars[1])) {
114
            return 'l';
115
        }
116
117
        if ($this->inRanges($int, $this->chars[2])) {
118
            return 'd';
119
        }
120
121
        if ($this->inRanges($int, $this->chars[3])) {
122
            return 's';
123
        }
124
    }
125
    
126
    /**
127
     * Generate a random password corresponding at the given topology.
128
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
129
     *
130
     * $passwordGenerator = new PasswordGenerator();
131
     * $random = $passwordGenerator->getFromTopology('ldusdusludusddldlsdd');
132
     *
133
     * //var_dump result
134
     * //r4Q,1J*tM7D_99q0u>61
135
     * var_dump($random);
136
     * </code></pre>
137
     *
138
     * @param string $topology Topology for generate password.
139
     * @return string Random password corresponding the given topology.
140
     *
141
     * @throws InvalidArgumentException If invalid pattern is provided.
142
     */
143
    public function getFromTopology(string $topology): string
144
    {
145
        $array = str_split(strtolower($topology));
146
        $password = [];
147
148
        foreach ($array as $char) {
149
            $int = ord($char);
150
151
            if (isset($this->groups[$int])) {
152
                $key = $this->groups[$int];
153
                $password[] = $this->getRandomChar($this->chars[$key]);
154
155
                continue;
156
            }
157
158
            throw new InvalidArgumentException('Invalid pattern provided, accepted only u, l, d and s');
159
        }
160
161
        return implode($password);
162
    }
163
164
    /**
165
     * Get random char between.
166
     *
167
     * @param array $interval
168
     * @return string
169
     */
170
    private function getRandomChar(array $interval): string
171
    {
172
        do {
173
            $int = random_int(33, 122);
174
            if ($this->inRanges($int, $interval)) {
175
                break;
176
            }
177
        } while (true);
178
            
179
        return chr($int);
180
    }
181
182
    /**
183
     * Check if value is between given range.
184
     *
185
     * @param mixed $value
186
     * @param array $ranges
187
     *
188
     * @return bool
189
     */
190
    private function inRanges($value, array $ranges): bool
191
    {
192
        foreach ($ranges as $range) {
193
            if ($value >= $range[0] && $value <= $range[1]) {
194
                return true;
195
            }
196
        }
197
198
        return false;
199
    }
200
}
201