Passed
Push — master ( 8bc73a...1a3e6a )
by Sebastian
02:43
created

PasswordGenerator::inRanges()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 5
nc 3
nop 2
crap 4
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 25
    public function getTopology(string $password): string
76
    {
77 25
        $array = str_split($password);
78 25
        $topology = [];
79
        
80 25
        foreach ($array as $char) {
81 25
            $topology[] = $this->getTopologyGroup($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 25
    private function getTopologyGroup(string $char) : string
95
    {
96 25
        $int = ord($char);
97 25
        $groups = ['u', 'l', 'd', 's'];
98
        
99 25
        foreach ($groups as $key => $group) {
100 25
            if ($this->inRanges($int, $this->chars[$key])) {
101 25
                return $group;
102
            }
103
        }
104
    }
105
    
106
    /**
107
     * Generate a random password corresponding at the given topology.
108
     * <pre><code class="php">use Linna\Auth\PasswordGenerator;
109
     *
110
     * $passwordGenerator = new PasswordGenerator();
111
     * $random = $passwordGenerator->getFromTopology('ldusdusludusddldlsdd');
112
     *
113
     * //var_dump result
114
     * //r4Q,1J*tM7D_99q0u>61
115
     * var_dump($random);
116
     * </code></pre>
117
     *
118
     * @param string $topology Topology for generate password.
119
     * @return string Random password corresponding the given topology.
120
     *
121
     * @throws InvalidArgumentException If invalid pattern is provided.
122
     */
123 16
    public function getFromTopology(string $topology): string
124
    {
125 16
        $array = str_split(strtolower($topology));
126 16
        $groups = [117 => 0, 108 => 1, 100 => 2, 115 => 3];
127 16
        $password = [];
128
129 16
        foreach ($array as $char) {
130 16
            $int = ord($char);
131
132 16
            if (isset($groups[$int])) {
133 13
                $password[] = $this->getRandomChar($this->chars[$groups[$int]]);
134
135 13
                continue;
136
            }
137
138 6
            throw new InvalidArgumentException('Invalid pattern provided, accepted only u, l, d and s');
139
        }
140
141 10
        return implode($password);
142
    }
143
144
    /**
145
     * Get random char between.
146
     *
147
     * @param array $interval
148
     * @return string
149
     */
150 21
    private function getRandomChar(array $interval): string
151
    {
152
        do {
153 21
            $int = random_int(33, 122);
154 21
            if ($this->inRanges($int, $interval)) {
155 21
                break;
156
            }
157 21
        } while (true);
158
            
159 21
        return chr($int);
160
    }
161
162
    /**
163
     * Check if value is between given range.
164
     *
165
     * @param mixed $value
166
     * @param array $ranges
167
     *
168
     * @return bool
169
     */
170 32
    private function inRanges($value, array $ranges): bool
171
    {
172 32
        foreach ($ranges as $range) {
173 32
            if ($value >= $range[0] && $value <= $range[1]) {
174 32
                return true;
175
            }
176
        }
177
178 32
        return false;
179
    }
180
}
181