1<?php
2namespace Hidehalo\Nanoid;
3
4class Client
5{
6    /**
7     * Random mode flags
8     *
9     * @const MODE_NORMAL 1
10     * @const MODEL_DYNAMIC 2
11     */
12    const MODE_NORMAL = 1;
13    const MODE_DYNAMIC = 2;
14    /**
15     * @param string $alphabet Symbols to be used in ID.
16     * @param integer $size number of symbols in ID.
17     */
18    protected $alphbet;
19    protected $size;
20
21    /**
22     * @var CoreInterface $core Core dynamic random
23     */
24    private $core;
25    /**
26     * @var GeneratorInterface $generator Random Btyes Generator
27     */
28    protected $generator;
29
30    /**
31     * Constructor of Client
32     *
33     * @codeCoverageIgnore
34     * @param integer $size
35     * @param GeneratorInterface $generator
36     */
37    public function __construct($size = 21, GeneratorInterface $generator = null)
38    {
39        $this->size = $size > 0 ? $size : 21;
40        $this->generator = $generator?:new Generator();
41        $this->core = new Core();
42        $this->alphbet = CoreInterface::SAFE_SYMBOLS;
43    }
44
45    /**
46     * Generate nanoid via optional modes
47     *
48     * @param integer $size
49     * @param integer $mode Client::MODE_NORMAL|Client::MODE_DYNAMIC
50     * @return string
51     */
52    public function generateId($size = 0, $mode = self::MODE_NORMAL)
53    {
54        $size = $size>0? $size: $this->size;
55        switch ($mode) {
56            case self::MODE_DYNAMIC:
57                return $this->core->random($this->generator, $size, $this->alphbet);
58            default:
59                return $this->normalRandom($size);
60        }
61    }
62
63    /**
64     * The original API of nanoid. Use it be careful, Please make sure
65     * you have been implements your custom GeneratorInterface as correctly.
66     * Otherwise use the build-in default random bytes generator
67     *
68     * @param GeneratorInterface $generator
69     * @param integer $size
70     * @param string $alphabet default CoreInterface::SAFE_SYMBOLS
71     * @return string
72     */
73    public function formattedId($alphabet, $size = 0, GeneratorInterface $generator = null)
74    {
75        $alphabet = $alphabet?:CoreInterface::SAFE_SYMBOLS;
76        $size = $size>0? $size: $this->size;
77        $generator = $generator?:$this->generator;
78
79        return $this->core->random($generator, $size, $alphabet);
80    }
81
82    /**
83     * Backwards-compatible method name.
84     *
85     * @param string $alphabet
86     * @param integer $size
87     * @param GeneratorInterface $generator
88     *
89     * @return string
90     * @since 1.0.0
91     */
92    public function formatedId($alphabet, $size = 0, GeneratorInterface $generator = null)
93    {
94        $size = $size>0? $size: $this->size;
95
96        return $this->formattedId($alphabet, $size, $generator);
97    }
98
99    /**
100     * Generate secure URL-friendly unique ID.
101     * By default, ID will have 21 symbols to have same collisions probability
102     * as UUID v4.
103     *
104     * @see https://github.com/ai/nanoid/blob/master/non-secure/index.js#L19
105     * @param integer $size
106     * @return string
107     */
108    private function normalRandom($size)
109    {
110        $id = '';
111        while (1 <= $size--) {
112            $rand = mt_rand()/(mt_getrandmax() + 1);
113            $id .= $this->alphbet[$rand*64 | 0];
114        }
115
116        return $id;
117    }
118}
119