1<?php
2
3/**
4 * This file is part of the ramsey/uuid library
5 *
6 * For the full copyright and license information, please view the LICENSE
7 * file that was distributed with this source code.
8 *
9 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
10 * @license http://opensource.org/licenses/MIT MIT
11 */
12
13declare(strict_types=1);
14
15namespace Ramsey\Uuid;
16
17use Ramsey\Uuid\Builder\BuilderCollection;
18use Ramsey\Uuid\Builder\FallbackBuilder;
19use Ramsey\Uuid\Builder\UuidBuilderInterface;
20use Ramsey\Uuid\Codec\CodecInterface;
21use Ramsey\Uuid\Codec\GuidStringCodec;
22use Ramsey\Uuid\Codec\StringCodec;
23use Ramsey\Uuid\Converter\Number\GenericNumberConverter;
24use Ramsey\Uuid\Converter\NumberConverterInterface;
25use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
26use Ramsey\Uuid\Converter\Time\PhpTimeConverter;
27use Ramsey\Uuid\Converter\TimeConverterInterface;
28use Ramsey\Uuid\Generator\DceSecurityGenerator;
29use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
30use Ramsey\Uuid\Generator\NameGeneratorFactory;
31use Ramsey\Uuid\Generator\NameGeneratorInterface;
32use Ramsey\Uuid\Generator\PeclUuidNameGenerator;
33use Ramsey\Uuid\Generator\PeclUuidRandomGenerator;
34use Ramsey\Uuid\Generator\PeclUuidTimeGenerator;
35use Ramsey\Uuid\Generator\RandomGeneratorFactory;
36use Ramsey\Uuid\Generator\RandomGeneratorInterface;
37use Ramsey\Uuid\Generator\TimeGeneratorFactory;
38use Ramsey\Uuid\Generator\TimeGeneratorInterface;
39use Ramsey\Uuid\Guid\GuidBuilder;
40use Ramsey\Uuid\Math\BrickMathCalculator;
41use Ramsey\Uuid\Math\CalculatorInterface;
42use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder;
43use Ramsey\Uuid\Provider\Dce\SystemDceSecurityProvider;
44use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
45use Ramsey\Uuid\Provider\Node\FallbackNodeProvider;
46use Ramsey\Uuid\Provider\Node\NodeProviderCollection;
47use Ramsey\Uuid\Provider\Node\RandomNodeProvider;
48use Ramsey\Uuid\Provider\Node\SystemNodeProvider;
49use Ramsey\Uuid\Provider\NodeProviderInterface;
50use Ramsey\Uuid\Provider\Time\SystemTimeProvider;
51use Ramsey\Uuid\Provider\TimeProviderInterface;
52use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder;
53use Ramsey\Uuid\Validator\GenericValidator;
54use Ramsey\Uuid\Validator\ValidatorInterface;
55
56use const PHP_INT_SIZE;
57
58/**
59 * FeatureSet detects and exposes available features in the current environment
60 *
61 * A feature set is used by UuidFactory to determine the available features and
62 * capabilities of the environment.
63 */
64class FeatureSet
65{
66    /**
67     * @var bool
68     */
69    private $disableBigNumber = false;
70
71    /**
72     * @var bool
73     */
74    private $disable64Bit = false;
75
76    /**
77     * @var bool
78     */
79    private $ignoreSystemNode = false;
80
81    /**
82     * @var bool
83     */
84    private $enablePecl = false;
85
86    /**
87     * @var UuidBuilderInterface
88     */
89    private $builder;
90
91    /**
92     * @var CodecInterface
93     */
94    private $codec;
95
96    /**
97     * @var DceSecurityGeneratorInterface
98     */
99    private $dceSecurityGenerator;
100
101    /**
102     * @var NameGeneratorInterface
103     */
104    private $nameGenerator;
105
106    /**
107     * @var NodeProviderInterface
108     */
109    private $nodeProvider;
110
111    /**
112     * @var NumberConverterInterface
113     */
114    private $numberConverter;
115
116    /**
117     * @var TimeConverterInterface
118     */
119    private $timeConverter;
120
121    /**
122     * @var RandomGeneratorInterface
123     */
124    private $randomGenerator;
125
126    /**
127     * @var TimeGeneratorInterface
128     */
129    private $timeGenerator;
130
131    /**
132     * @var TimeProviderInterface
133     */
134    private $timeProvider;
135
136    /**
137     * @var ValidatorInterface
138     */
139    private $validator;
140
141    /**
142     * @var CalculatorInterface
143     */
144    private $calculator;
145
146    /**
147     * @param bool $useGuids True build UUIDs using the GuidStringCodec
148     * @param bool $force32Bit True to force the use of 32-bit functionality
149     *     (primarily for testing purposes)
150     * @param bool $forceNoBigNumber True to disable the use of moontoast/math
151     *     (primarily for testing purposes)
152     * @param bool $ignoreSystemNode True to disable attempts to check for the
153     *     system node ID (primarily for testing purposes)
154     * @param bool $enablePecl True to enable the use of the PeclUuidTimeGenerator
155     *     to generate version 1 UUIDs
156     */
157    public function __construct(
158        bool $useGuids = false,
159        bool $force32Bit = false,
160        bool $forceNoBigNumber = false,
161        bool $ignoreSystemNode = false,
162        bool $enablePecl = false
163    ) {
164        $this->disableBigNumber = $forceNoBigNumber;
165        $this->disable64Bit = $force32Bit;
166        $this->ignoreSystemNode = $ignoreSystemNode;
167        $this->enablePecl = $enablePecl;
168
169        $this->setCalculator(new BrickMathCalculator());
170        $this->builder = $this->buildUuidBuilder($useGuids);
171        $this->codec = $this->buildCodec($useGuids);
172        $this->nodeProvider = $this->buildNodeProvider();
173        $this->nameGenerator = $this->buildNameGenerator();
174        $this->randomGenerator = $this->buildRandomGenerator();
175        $this->setTimeProvider(new SystemTimeProvider());
176        $this->setDceSecurityProvider(new SystemDceSecurityProvider());
177        $this->validator = new GenericValidator();
178    }
179
180    /**
181     * Returns the builder configured for this environment
182     */
183    public function getBuilder(): UuidBuilderInterface
184    {
185        return $this->builder;
186    }
187
188    /**
189     * Returns the calculator configured for this environment
190     */
191    public function getCalculator(): CalculatorInterface
192    {
193        return $this->calculator;
194    }
195
196    /**
197     * Returns the codec configured for this environment
198     */
199    public function getCodec(): CodecInterface
200    {
201        return $this->codec;
202    }
203
204    /**
205     * Returns the DCE Security generator configured for this environment
206     */
207    public function getDceSecurityGenerator(): DceSecurityGeneratorInterface
208    {
209        return $this->dceSecurityGenerator;
210    }
211
212    /**
213     * Returns the name generator configured for this environment
214     */
215    public function getNameGenerator(): NameGeneratorInterface
216    {
217        return $this->nameGenerator;
218    }
219
220    /**
221     * Returns the node provider configured for this environment
222     */
223    public function getNodeProvider(): NodeProviderInterface
224    {
225        return $this->nodeProvider;
226    }
227
228    /**
229     * Returns the number converter configured for this environment
230     */
231    public function getNumberConverter(): NumberConverterInterface
232    {
233        return $this->numberConverter;
234    }
235
236    /**
237     * Returns the random generator configured for this environment
238     */
239    public function getRandomGenerator(): RandomGeneratorInterface
240    {
241        return $this->randomGenerator;
242    }
243
244    /**
245     * Returns the time converter configured for this environment
246     */
247    public function getTimeConverter(): TimeConverterInterface
248    {
249        return $this->timeConverter;
250    }
251
252    /**
253     * Returns the time generator configured for this environment
254     */
255    public function getTimeGenerator(): TimeGeneratorInterface
256    {
257        return $this->timeGenerator;
258    }
259
260    /**
261     * Returns the validator configured for this environment
262     */
263    public function getValidator(): ValidatorInterface
264    {
265        return $this->validator;
266    }
267
268    /**
269     * Sets the calculator to use in this environment
270     */
271    public function setCalculator(CalculatorInterface $calculator): void
272    {
273        $this->calculator = $calculator;
274        $this->numberConverter = $this->buildNumberConverter($calculator);
275        $this->timeConverter = $this->buildTimeConverter($calculator);
276
277        if (isset($this->timeProvider)) {
278            $this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
279        }
280    }
281
282    /**
283     * Sets the DCE Security provider to use in this environment
284     */
285    public function setDceSecurityProvider(DceSecurityProviderInterface $dceSecurityProvider): void
286    {
287        $this->dceSecurityGenerator = $this->buildDceSecurityGenerator($dceSecurityProvider);
288    }
289
290    /**
291     * Sets the node provider to use in this environment
292     */
293    public function setNodeProvider(NodeProviderInterface $nodeProvider): void
294    {
295        $this->nodeProvider = $nodeProvider;
296        $this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
297    }
298
299    /**
300     * Sets the time provider to use in this environment
301     */
302    public function setTimeProvider(TimeProviderInterface $timeProvider): void
303    {
304        $this->timeProvider = $timeProvider;
305        $this->timeGenerator = $this->buildTimeGenerator($timeProvider);
306    }
307
308    /**
309     * Set the validator to use in this environment
310     */
311    public function setValidator(ValidatorInterface $validator): void
312    {
313        $this->validator = $validator;
314    }
315
316    /**
317     * Returns a codec configured for this environment
318     *
319     * @param bool $useGuids Whether to build UUIDs using the GuidStringCodec
320     */
321    private function buildCodec(bool $useGuids = false): CodecInterface
322    {
323        if ($useGuids) {
324            return new GuidStringCodec($this->builder);
325        }
326
327        return new StringCodec($this->builder);
328    }
329
330    /**
331     * Returns a DCE Security generator configured for this environment
332     */
333    private function buildDceSecurityGenerator(
334        DceSecurityProviderInterface $dceSecurityProvider
335    ): DceSecurityGeneratorInterface {
336        return new DceSecurityGenerator(
337            $this->numberConverter,
338            $this->timeGenerator,
339            $dceSecurityProvider
340        );
341    }
342
343    /**
344     * Returns a node provider configured for this environment
345     */
346    private function buildNodeProvider(): NodeProviderInterface
347    {
348        if ($this->ignoreSystemNode) {
349            return new RandomNodeProvider();
350        }
351
352        return new FallbackNodeProvider(new NodeProviderCollection([
353            new SystemNodeProvider(),
354            new RandomNodeProvider(),
355        ]));
356    }
357
358    /**
359     * Returns a number converter configured for this environment
360     */
361    private function buildNumberConverter(CalculatorInterface $calculator): NumberConverterInterface
362    {
363        return new GenericNumberConverter($calculator);
364    }
365
366    /**
367     * Returns a random generator configured for this environment
368     */
369    private function buildRandomGenerator(): RandomGeneratorInterface
370    {
371        if ($this->enablePecl) {
372            return new PeclUuidRandomGenerator();
373        }
374
375        return (new RandomGeneratorFactory())->getGenerator();
376    }
377
378    /**
379     * Returns a time generator configured for this environment
380     *
381     * @param TimeProviderInterface $timeProvider The time provider to use with
382     *     the time generator
383     */
384    private function buildTimeGenerator(TimeProviderInterface $timeProvider): TimeGeneratorInterface
385    {
386        if ($this->enablePecl) {
387            return new PeclUuidTimeGenerator();
388        }
389
390        return (new TimeGeneratorFactory(
391            $this->nodeProvider,
392            $this->timeConverter,
393            $timeProvider
394        ))->getGenerator();
395    }
396
397    /**
398     * Returns a name generator configured for this environment
399     */
400    private function buildNameGenerator(): NameGeneratorInterface
401    {
402        if ($this->enablePecl) {
403            return new PeclUuidNameGenerator();
404        }
405
406        return (new NameGeneratorFactory())->getGenerator();
407    }
408
409    /**
410     * Returns a time converter configured for this environment
411     */
412    private function buildTimeConverter(CalculatorInterface $calculator): TimeConverterInterface
413    {
414        $genericConverter = new GenericTimeConverter($calculator);
415
416        if ($this->is64BitSystem()) {
417            return new PhpTimeConverter($calculator, $genericConverter);
418        }
419
420        return $genericConverter;
421    }
422
423    /**
424     * Returns a UUID builder configured for this environment
425     *
426     * @param bool $useGuids Whether to build UUIDs using the GuidStringCodec
427     */
428    private function buildUuidBuilder(bool $useGuids = false): UuidBuilderInterface
429    {
430        if ($useGuids) {
431            return new GuidBuilder($this->numberConverter, $this->timeConverter);
432        }
433
434        /** @psalm-suppress ImpureArgument */
435        return new FallbackBuilder(new BuilderCollection([
436            new Rfc4122UuidBuilder($this->numberConverter, $this->timeConverter),
437            new NonstandardUuidBuilder($this->numberConverter, $this->timeConverter),
438        ]));
439    }
440
441    /**
442     * Returns true if the PHP build is 64-bit
443     */
444    private function is64BitSystem(): bool
445    {
446        return PHP_INT_SIZE === 8 && !$this->disable64Bit;
447    }
448}
449