Completed
Push — master ( ca4962...5234b9 )
by BENOIT
03:32
created

StringCombinations::withoutDuplicates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BenTools\StringCombinations;
4
5
use function BenTools\CartesianProduct\cartesian_product;
6
use IteratorAggregate;
7
8
/**
9
 * @property $min
10
 * @property $max
11
 * @property $charset
12
 * @property $glue
13
 */
14
final class StringCombinations implements IteratorAggregate, StringCombinationsInterface
15
{
16
    /**
17
     * @var string[]
18
     */
19
    private $charset;
20
21
    /**
22
     * @var int
23
     */
24
    private $min;
25
26
    /**
27
     * @var int
28
     */
29
    private $max;
30
31
    /**
32
     * @var int
33
     */
34
    private $count;
35
36
    /**
37
     * @var string
38
     */
39
    private $glue;
40
41
    /**
42
     * StringCombination constructor.
43
     * @param mixed  $charset
44
     * @param int    $min
45
     * @param int    $max
46
     * @param string $glue
47
     * @throws \InvalidArgumentException
48
     */
49
    public function __construct($charset, $min = 1, $max = null, $glue = '')
50
    {
51
        if (is_string($charset) || is_int($charset)) {
52
            $this->charset = preg_split('/(?<!^)(?!$)/u', $charset);
53
            $this->validateCharset($this->charset);
54
        } elseif (is_array($charset)) {
55
            $this->charset = $charset;
56
            $this->validateCharset($this->charset);
57
        } else {
58
            $this->denyCharset();
59
        }
60
        $this->min = (int) $min;
61
        $length = count($this->charset);
62
        $this->max = null === $max ? $length : min((int) $max, $this->charset);
0 ignored issues
show
Documentation Bug introduced by
It seems like null === $max ? $length ...) $max, $this->charset) can also be of type array. However, the property $max is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
63
        $this->glue = $glue;
64
    }
65
66
    /**
67
     * @return NoDuplicateLettersStringCombinations
68
     */
69
    public function withoutDuplicates()
70
    {
71
        return new NoDuplicateLettersStringCombinations($this);
72
    }
73
74
    /**
75
     * @inheritDoc
76
     */
77
    public function count()
78
    {
79
        if (null === $this->count) {
80
            $this->count = array_sum(array_map(function ($set) {
0 ignored issues
show
Documentation Bug introduced by
It seems like array_sum(array_map(func...this->generateSets()))) can also be of type double. However, the property $count is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
81
                return count(cartesian_product($set));
82
            }, iterator_to_array($this->generateSets())));
83
        }
84
        return $this->count;
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    public function getIterator()
91
    {
92
        foreach ($this->generateSets() as $set) {
93
            foreach (cartesian_product($set) as $combination) {
94
                yield implode($this->glue, $combination);
95
            }
96
        }
97
    }
98
99
    /**
100
     * Creates a random string from current charset
101
     * @return string
102
     */
103
    public function getRandomString()
104
    {
105
        $length = random_int($this->min, $this->max);
106
        $charset = $this->charset;
107
        for ($pos = 0, $str = []; $pos < $length; $pos++) {
108
            shuffle($charset);
109
            $str[] = $charset[0];
110
        }
111
        return implode($this->glue, $str);
112
    }
113
114
    /**
115
     * @return array
116
     */
117
    public function asArray()
118
    {
119
        return iterator_to_array($this);
120
    }
121
122
    /**
123
     * @return \Generator
124
     */
125
    private function generateSets()
126
    {
127
        for ($i = $this->min; $i <= $this->max; $i++) {
128
            $set = array_fill(0, $i, $this->charset);
129
            yield $set;
130
        }
131
    }
132
133
    private function validateCharset($charset)
134
    {
135
        if (null === $charset) {
136
            $this->denyCharset();
137
        }
138
        foreach ($charset as $value) {
139
            if (!is_string($value) && !is_integer($value)) {
140
                $this->denyCharset();
141
            }
142
        }
143
    }
144
145
    /**
146
     * @throws \InvalidArgumentException
147
     */
148
    private function denyCharset()
149
    {
150
        throw new \InvalidArgumentException('Charset should be a string or an array of strings.');
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156
    public function __get($name)
157
    {
158
        return $this->{$name};
159
    }
160
}
161