Completed
Push — master ( 4d4232...ffcfa4 )
by Anthony
14:02 queued 11:53
created

lib/RandomLib/Source/MicroTime.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * The Microtime Random Number Source
4
 *
5
 * This uses the current micro-second (looped several times) for a **very** weak
6
 * random number source.  This is only useful when combined with several other
7
 * stronger sources
8
 *
9
 * PHP version 5.3
10
 *
11
 * @category   PHPCryptLib
12
 * @package    Random
13
 * @subpackage Source
14
 * @author     Anthony Ferrara <[email protected]>
15
 * @copyright  2011 The Authors
16
 * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
17
 * @version    Build @@version@@
18
 */
19
20
namespace RandomLib\Source;
21
22
use SecurityLib\Strength;
23
use SecurityLib\Util;
24
25
/**
26
 * The Microtime Random Number Source
27
 *
28
 * This uses the current micro-second (looped several times) for a **very** weak
29
 * random number source.  This is only useful when combined with several other
30
 * stronger sources
31
 *
32
 * @category   PHPCryptLib
33
 * @package    Random
34
 * @subpackage Source
35
 * @author     Anthony Ferrara <[email protected]>
36
 * @codeCoverageIgnore
37
 */
38
final class MicroTime implements \RandomLib\Source {
39
40
    /**
41
     * A static counter to ensure unique hashes and prevent state collisions
42
     * @var int A counter
43
     */
44
    private static $counter = null;
45
46
    /**
47
     * The current state of the random number generator.
48
     * @var string The state of the PRNG
49
     */
50
    private static $state = '';
51
52
    /**
53
     * Return an instance of Strength indicating the strength of the source
54
     *
55
     * @return Strength An instance of one of the strength classes
56
     */
57
    public static function getStrength() {
58
        return new Strength(Strength::VERYLOW);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \SecurityLib\...Lib\Strength::VERYLOW); (SecurityLib\Strength) is incompatible with the return type declared by the interface RandomLib\Source::getStrength of type RandomLib\Strength.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
59
    }
60
61
    public function __construct() {
62
        $state = self::$state;
63
        if (function_exists('posix_times')) {
64
            $state .= serialize(posix_times());
65
        }
66
        if (!defined('HHVM_VERSION') && function_exists('zend_thread_id')) {
67
            $state .= zend_thread_id();
68
        }
69
        if (function_exists('hphp_get_thread_id')) {
70
            $state .= hphp_get_thread_id();
71
        }
72
        $state      .= getmypid() . memory_get_usage();
73
        $state      .= serialize($_ENV);
74
        $state      .= serialize($_SERVER);
75
        $state      .= count(debug_backtrace(false));
76
        self::$state = hash('sha512', $state, true);
77
        if (is_null(self::$counter)) {
78
            list( , self::$counter) = unpack("i", Util::safeSubstr(self::$state, 0, 4));
79
            $seed = $this->generate(Util::safeStrlen(dechex(PHP_INT_MAX)));
80
            list( , self::$counter) = unpack("i", $seed);
81
        }
82
    }
83
84
    /**
85
     * Generate a random string of the specified size
86
     *
87
     * @param int $size The size of the requested random string
88
     *
89
     * @return string A string of the requested size
90
     */
91
    public function generate($size) {
92
        $result      = '';
93
        $seed        = microtime() . memory_get_usage();
94
        self::$state = hash('sha512', self::$state . $seed, true);
95
        /**
96
         * Make the generated randomness a bit better by forcing a GC run which
97
         * should complete in a indeterminate amount of time, hence improving
98
         * the strength of the randomness a bit. It's still not crypto-safe,
99
         * but at least it's more difficult to predict.
100
         */
101
        gc_collect_cycles();
102
        for ($i = 0; $i < $size; $i += 8) {
103
            $seed = self::$state .
104
                    microtime() .
105
                    pack('Ni', $i, self::counter());
106
            self::$state = hash('sha512', $seed, true);
107
            /**
108
             * We only use the first 8 bytes here to prevent exposing the state
109
             * in its entirety, which could potentially expose other random 
110
             * generations in the future (in the same process)...
111
             */
112
            $result .= Util::safeSubstr(self::$state, 0, 8);
113
        }
114
        return Util::safeSubstr($result, 0, $size);
115
    }
116
117
    private static function counter() {
118
        if (self::$counter >= PHP_INT_MAX) {
119
            self::$counter = -1 * PHP_INT_MAX - 1;
120
        } else {
121
            self::$counter++;
122
        }
123
        return self::$counter;
124
    }
125
126
}
127