--- /dev/null
+<?php
+/**
+ * The Random Number Generator Class
+ *
+ * Use this factory to generate cryptographic quality random numbers (strings)
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @author Timo Hamina
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace CryptLib\Random;
+
+use CryptLib\Core\BaseConverter;
+
+/**
+ * The Random Number Generator Class
+ *
+ * Use this factory to generate cryptographic quality random numbers (strings)
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @author Timo Hamina
+ */
+class Generator {
+
+ /**
+ * @var Mixer The mixing strategy to use for this generator instance
+ */
+ protected $mixer = null;
+
+ /**
+ * @var array An array of random number sources to use for this generator
+ */
+ protected $sources = array();
+
+ /**
+ * Build a new instance of the generator
+ *
+ * @param array $sources An array of random data sources to use
+ * @param Mixer $mixer The mixing strategy to use for this generator
+ */
+ public function __construct(array $sources, Mixer $mixer) {
+ foreach ($sources as $source) {
+ $this->addSource($source);
+ }
+ $this->mixer = $mixer;
+ }
+
+ /**
+ * Add a random number source to the generator
+ *
+ * @param Source $source The random number source to add
+ *
+ * @return Generator $this The current generator instance
+ */
+ public function addSource(Source $source) {
+ $this->sources[] = $source;
+ return $this;
+ }
+
+ /**
+ * Generate a random number (string) of the requested size
+ *
+ * @param int $size The size of the requested random number
+ *
+ * @return string The generated random number (string)
+ */
+ public function generate($size) {
+ $seeds = array();
+ foreach ($this->sources as $source) {
+ $seeds[] = $source->generate($size);
+ }
+ return $this->mixer->mix($seeds);
+ }
+
+ /**
+ * Generate a random integer with the given range
+ *
+ * @param int $min The lower bound of the range to generate
+ * @param int $max The upper bound of the range to generate
+ *
+ * @return int The generated random number within the range
+ */
+ public function generateInt($min = 0, $max = PHP_INT_MAX) {
+ $tmp = (int) max($max, $min);
+ $min = (int) min($max, $min);
+ $max = $tmp;
+ $range = $max - $min;
+ if ($range == 0) {
+ return $max;
+ } elseif ($range > PHP_INT_MAX || is_float($range)) {
+ /**
+ * This works, because PHP will auto-convert it to a float at this point,
+ * But on 64 bit systems, the float won't have enough precision to
+ * actually store the difference, so we need to check if it's a float
+ * and hence auto-converted...
+ */
+ throw new \RangeException(
+ 'The supplied range is too great to generate'
+ );
+ }
+
+ $bits = (int) floor(log($range, 2) + 1);
+ $bytes = (int) max(ceil($bits / 8), 1);
+ $mask = (int) (pow(2, $bits) - 1);
+ /**
+ * The mask is a better way of dropping unused bits. Basically what it does
+ * is to set all the bits in the mask to 1 that we may need. Since the max
+ * range is PHP_INT_MAX, we will never need negative numbers (which would
+ * have the MSB set on the max int possible to generate). Therefore we
+ * can just mask that away. Since pow returns a float, we need to cast
+ * it back to an int so the mask will work.
+ *
+ * On a 64 bit platform, that means that PHP_INT_MAX is 2^63 - 1. Which
+ * is also the mask if 63 bits are needed (by the log(range, 2) call).
+ * So if the computed result is negative (meaning the 64th bit is set), the
+ * mask will correct that.
+ *
+ * This turns out to be slightly better than the shift as we don't need to
+ * worry about "fixing" negative values.
+ */
+ do {
+ $test = $this->generate($bytes);
+ $result = hexdec(bin2hex($test)) & $mask;
+ } while ($result > $range);
+ return $result + $min;
+ }
+
+ /**
+ * Generate a random string of specified length.
+ *
+ * This uses the supplied character list for generating the new result
+ * string.
+ *
+ * @param int $length The length of the generated string
+ * @param string $characters An optional list of characters to use
+ *
+ * @return string The generated random string
+ */
+ public function generateString($length, $characters = '') {
+ if ($length == 0 || strlen($characters) == 1) {
+ return '';
+ } elseif (empty($characters)) {
+ // Default to base 64
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyz' .
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ./';
+ }
+ //determine how many bytes to generate
+ $bytes = ceil($length * floor(log(strlen($characters), 2) + 1.01) / 8);
+ $rand = $this->generate($bytes);
+ $result = BaseConverter::convertFromBinary($rand, $characters);
+ if (strlen($result) < $length) {
+ $result = str_pad($result, $length, $characters[0], STR_PAD_LEFT);
+ } else {
+ $result = substr($result, 0, $length);
+ }
+ return $result;
+ }
+
+ /**
+ * Get the Mixer used for this instance
+ *
+ * @return Mixer the current mixer
+ */
+ public function getMixer() {
+ return $this->mixer;
+ }
+
+ /**
+ * Get the Sources used for this instance
+ *
+ * @return Source[] the current mixer
+ */
+ public function getSources() {
+ return $this->sources;
+ }
+
+}