Completed
Push — master ( da2629...3965b3 )
by Michael
03:47 queued 02:20
created

Random::shuffle()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.0312

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 32
ccs 14
cts 16
cp 0.875
rs 8.5806
cc 4
eloc 15
nc 4
nop 3
crap 4.0312
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 20
    private static function fromMcrypt($bytes)
39
    {
40 20
        $ret = \mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
41
42 20
        if ($ret === false) {
43
            self::toss(); // @codeCoverageIgnore
44
        }
45
46 20
        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 20
    public static function bytes($bytes)
57
    {
58 20
        if (\function_exists('random_bytes')) {
59
            return \random_bytes($bytes);
60 20
        } elseif (\function_exists('mcrypt_create_iv')) {
61 20
            return self::fromMcrypt($bytes);
62
        }
63
        
64
        self::toss(); // @codeCoverageIgnore
65
    }
66
67
    /**
68
     * Throw an error when a failure occurs.
69
     * 
70
     * @codeCoverageIgnore
71
     */
72
    private static function toss()
73
    {
74
        $e = 'Dcrypt failed to generate a random number';
75
        throw new \exception($e);
76
    }
77
    
78
79
    /**
80
     * Deterministic seeded array shuffle function.
81
     *
82
     * @param array  $array
83
     * @param string $seed
84
     * @param bool   $secure
85
     *
86
     * @return array
87
     */
88 1
    public static function shuffle($array, $seed, $secure = true)
89
    {
90 1
        $count = count($array);
91
92 1
        $range = range(0, $count - 1);
93
94
        // Hash the seed and extract bytes to make integer with
95 1
        $seed = substr(hash('sha256', $seed, true), 0, PHP_INT_SIZE);
96
97
        // Convert bytes to an int
98 1
        $seed = unpack("L", $seed);
99
100 1
        if (version_compare(PHP_VERSION, '7.1.0') >= 0 && $secure === false) {
101
            // Handle PHP 7.1+ calls requiring the old implementation which has broken implementation
102
            mt_srand($seed[1], MT_RAND_PHP);
103
        } else {
104 1
            mt_srand($seed[1]);
105
        }
106
107
        // Swap array values randomly
108 1
        foreach ($range as $a) {
109 1
            $b = mt_rand(0, $count - 1);
110
111 1
            $v = $array[$a];
112
113 1
            $array[$a] = $array[$b];
114
115 1
            $array[$b] = $v;
116 1
        }
117
118 1
        return $array;
119
    }
120
}
121