Completed
Push — master ( e3397a...3c0a55 )
by Maxime
02:05 queued 11s
created

EnumProvider::randomEnums()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 4
nc 4
nop 4
1
<?php
2
3
/*
4
 * This file is part of the "elao/enum" package.
5
 *
6
 * Copyright (C) Elao
7
 *
8
 * @author Elao <[email protected]>
9
 */
10
11
namespace Elao\Enum\Bridge\Faker\Provider;
12
13
use Elao\Enum\EnumInterface;
14
use Elao\Enum\Exception\InvalidArgumentException;
15
use Elao\Enum\FlaggedEnum;
16
17
class EnumProvider
18
{
19
    /**
20
     * Enum mapping as an array with :
21
     *
22
     * - alias for Enum class as key
23
     * - Enum FQCN as value
24
     *
25
     * Example :
26
     *
27
     * ```
28
     * [
29
     *   'Civility' => Civility::class,
30
     *   'Gender'   => Gender::class,
31
     * ]
32
     * ```
33
     *
34
     * @var array
35
     */
36
    private $enumMapping = [];
37
38
    public function __construct(array $enumMapping = [])
39
    {
40
        foreach ($enumMapping as $enumAlias => $enumClass) {
41
            $this->ensureEnumClass($enumClass);
42
            $this->enumMapping[$enumAlias] = $enumClass;
43
        }
44
    }
45
46
    /**
47
     * @param string $enumValueShortcut As <ENUM_CLASS_ALIAS_OR_ENUM_FCQN>::<ENUM_VALUE_CONSTANT>
48
     *                                  Examples: 'Gender::MALE', 'App\Enum\Gender::FEMALE', 'Permissions::READ|WRITE', etc.
49
     *
50
     * @throws InvalidArgumentException When the alias part of $enumValueShortcut is not a valid alias
51
     */
52
    public function enum(string $enumValueShortcut): EnumInterface
53
    {
54
        list($enumClassOrAlias, $constants) = explode('::', $enumValueShortcut);
55
56
        /** @var EnumInterface|string $class */
57
        $class = $this->enumMapping[$enumClassOrAlias] ?? $enumClassOrAlias;
58
        $this->ensureEnumClass($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object<Elao\Enum\EnumInterface>; however, Elao\Enum\Bridge\Faker\P...ider::ensureEnumClass() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
59
60
        $constants = explode('|', $constants);
61
62
        // Flagged Enum if $constants count is greater than one:
63
        if (\count($constants) > 1) {
64
            if (!is_a($class, FlaggedEnum::class, true)) {
65
                throw new InvalidArgumentException("$class is not a valid FlaggedEnum");
66
            }
67
            $value = 0;
68
            foreach ($constants as $constant) {
69
                $value |= \constant($class . '::' . $constant);
70
            }
71
        } else {
72
            $value = \constant($class . '::' . current($constants));
73
        }
74
75
        return $class::get($value);
76
    }
77
78
    /**
79
     * @throws InvalidArgumentException When $enumClassAlias is not a valid alias
80
     */
81
    public function randomEnum(string $enumClassOrAlias): EnumInterface
82
    {
83
        /** @var EnumInterface|string $class */
84
        $class = $this->enumMapping[$enumClassOrAlias] ?? $enumClassOrAlias;
85
        $this->ensureEnumClass($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object<Elao\Enum\EnumInterface>; however, Elao\Enum\Bridge\Faker\P...ider::ensureEnumClass() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
86
87
        $instances = $class::instances();
88
        $randomRank = random_int(0, \count($instances) - 1);
89
90
        return $instances[$randomRank];
91
    }
92
93
    /**
94
     * Generate an array of unique enum instances between $count & $min.
95
     *
96
     * @param int  $count    The max nb of enums to generate
97
     * @param bool $variable If false, the exact $count of enums will be generated
98
     *                       (unless there is no more unique value available)
99
     * @param int  $min      The min nb of enums to generate, if variable
100
     *
101
     * @return EnumInterface[]
102
     */
103
    public function randomEnums(string $enumClassOrAlias, int $count, bool $variable = true, int $min = 0): array
104
    {
105
        $enums = [];
106
107
        if ($variable) {
108
            $count = random_int($min, $count);
109
        }
110
111
        /** @var EnumInterface|string $class */
112
        $class = $this->enumMapping[$enumClassOrAlias] ?? $enumClassOrAlias;
113
        $this->ensureEnumClass($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object<Elao\Enum\EnumInterface>; however, Elao\Enum\Bridge\Faker\P...ider::ensureEnumClass() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
114
115
        $instances = $class::instances();
116
117
        while ($count > 0 && !empty($instances)) {
118
            $randomRank = random_int(0, \count($instances) - 1);
119
            $enums[] = $instances[$randomRank];
120
            unset($instances[$randomRank]);
121
            $instances = array_values($instances);
122
123
            --$count;
124
        }
125
126
        return $enums;
127
    }
128
129
    /**
130
     * Make sure that $enumClass is a proper Enum class. Throws exception otherwise.
131
     *
132
     * @throws InvalidArgumentException When $enumClass is not a class or is not a proper Enum
133
     */
134
    private function ensureEnumClass(string $enumClass)
135
    {
136
        if (!is_a($enumClass, EnumInterface::class, true)) {
137
            throw new InvalidArgumentException("$enumClass is not a proper enum class");
138
        }
139
    }
140
}
141