Completed
Push — master ( 76010e...11ad28 )
by Michael
01:32
created

Random   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Test Coverage

Coverage 81.48%

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 11
c 8
b 0
f 1
lcom 0
cbo 1
dl 0
loc 96
ccs 22
cts 27
cp 0.8148
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A fromMcrypt() 0 10 2
A bytes() 0 14 4
A toss() 0 5 1
B shuffle() 0 32 4
1
<?php
2
3
/**
4
 * Random.php
5
 * 
6
 * PHP version 5
7
 * 
8
 * @category Dcrypt
9
 * @package  Dcrypt
10
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
11
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
12
 * @link     https://github.com/mmeyer2k/dcrypt
13
 * @link     https://apigen.ci/github/mmeyer2k/dcrypt
14
 */
15
16
namespace Dcrypt;
17
18
/**
19
 * Fail-safe wrapper for mcrypt_create_iv (preferably) and
20
 * openssl_random_pseudo_bytes (fallback).
21
 *
22
 * @category Dcrypt
23
 * @package  Dcrypt
24
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
25
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
26
 * @link     https://github.com/mmeyer2k/dcrypt
27
 * @link     https://apigen.ci/github/mmeyer2k/dcrypt/class-Dcrypt.Random.html
28
 */
29
final class Random
30
{
31
    /**
32
     * Get random bytes from Mcrypt
33
     * 
34
     * @param int $bytes Number of bytes to get
35
     * 
36
     * @return string
37
     */
38 19
    private static function fromMcrypt($bytes)
39
    {
40 19
        $ret = \mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
41
42 19
        if ($ret === false) {
43
            self::toss(); // @codeCoverageIgnore
44
        }
45
46 19
        return $ret;
47
    }
48
49
    /**
50
     * Return securely generated random bytes.
51
     * 
52
     * @param int  $bytes  Number of bytes to get
53
     * 
54
     * @return string
55
     */
56 19
    public static function bytes($bytes)
57
    {
58 19
        if (!\is_int($bytes)) {
59
            throw new \exception('Number of random bytes must be an integer');
60 19
        }
61 19
        
62
        $ret = \function_exists('random_bytes') ? \random_bytes($bytes) : self::fromMcrypt($bytes);
63
        
64
        if (Str::strlen($ret) !== $bytes) {
65
            self::toss(); // @codeCoverageIgnore
66
        }
67
        
68
        return $ret;
69
    }
70
71
    /**
72
     * Throw an error when a failure occurs.
73
     * 
74
     * @codeCoverageIgnore
75
     */
76
    private static function toss()
77
    {
78
        $e = 'Dcrypt failed to generate a random number';
79
        throw new \exception($e);
80
    }
81
    
82
83
    /**
84
     * Deterministic seeded array shuffle function. Does not keep keys.
85
     *
86
     * @param array  $array  Array to shuffle
87
     * @param string $seed   Seed to use 
88 1
     * @param bool   $secure Whether to use secure RNG in PHP 7.1+. Use false to fall back to broken version for BC.
89
     *
90 1
     * @return array
91
     */
92 1
    public static function shuffle($array, $seed, $secure = true)
93
    {
94
        $count = \count($array);
95 1
96
        $range = \range(0, $count - 1);
97
98 1
        // Hash the seed and extract bytes to make integer with
99
        $seed = Str::substr(\hash('sha256', $seed, true), 0, PHP_INT_SIZE);
100 1
101
        // Convert bytes to an int
102
        $seed = \unpack("L", $seed);
103
104 1
        if (\version_compare(PHP_VERSION, '7.1.0') >= 0 && $secure === false) {
105
            // Handle PHP 7.1+ calls requiring the old implementation which has broken implementation
106
            \mt_srand($seed[1], MT_RAND_PHP);
107
        } else {
108 1
            \mt_srand($seed[1]);
109 1
        }
110
111 1
        // Swap array values randomly
112
        foreach ($range as $a) {
113 1
            $b = \mt_rand(0, $count - 1);
114
115 1
            $v = $array[$a];
116 1
117
            $array[$a] = $array[$b];
118 1
119
            $array[$b] = $v;
120
        }
121
122
        return $array;
123
    }
124
}
125