NameGenerator::generateFromInternalFile()   C
last analyzed

Complexity

Conditions 12
Paths 21

Size

Total Lines 43
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 37
c 1
b 0
f 0
dl 0
loc 43
rs 6.9666
cc 12
nc 21
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace GilDrd\NameGenerator;
4
5
use GilDrd\NameGenerator\Exceptions\GenerateFromFileException;
6
use GilDrd\NameGenerator\Exceptions\IntegrityException;
7
use GilDrd\NameGenerator\Tools\IntegrityChecker;
8
use GilDrd\NameGenerator\Tools\Parameter;
9
use GilDrd\NameGenerator\Tools\Type;
10
11
class NameGenerator
12
{
13
    private array $firstLetters;
14
    private array $possibleNextLetters;
15
    private array $nameList;
16
17
    private Parameter $parameter;
18
19
    public function __construct(?int ... $example)
20
    {
21
        $this->firstLetters = [];
22
        $this->possibleNextLetters = [];
23
        $this->parameter = new Parameter();
24
        $this->nameList = [];
25
26
        foreach (func_get_args() as $args) {
27
            $this->generateFromInternalFile($args);
28
        }
29
        
30
        if (!empty($this->nameList)) {
31
            $this->analyseFromArray($this->nameList);
32
        }
33
    }
34
    
35
    public function getNameListLength(): int
36
    {
37
        return count($this->nameList);
38
    }
39
40
    private function generateFromInternalFile(int $example): void
41
    {
42
        $exampleDirectory = __DIR__.'/examples';
43
44
        switch ($example) {
45
            case Type::MALE_40K_ARCHAIC:
46
                $handle = fopen($exampleDirectory.'/40k_male_archaic.csv', 'r');
47
                break;
48
            case Type::MALE_40K_LOWER_GOTHIC:
49
                $handle = fopen($exampleDirectory.'/40k_male_lower_gothic.csv', 'r');
50
                break;
51
            case Type::MALE_40K_HIGHER_GOTHIC:
52
                $handle = fopen($exampleDirectory.'/40k_male_higher_gothic.csv', 'r');
53
                break;
54
            case Type::MALE_40K_PRIMITIVE:
55
                $handle = fopen($exampleDirectory.'/40k_male_primitive.csv', 'r');
56
                break;
57
            case Type::FEMALE_40K_ARCHAIC:
58
                $handle = fopen($exampleDirectory.'/40k_female_archaic.csv', 'r');
59
                break;
60
            case Type::FEMALE_40K_LOWER_GOTHIC:
61
                $handle = fopen($exampleDirectory.'/40k_female_lower_gothic.csv', 'r');
62
                break;
63
            case Type::FEMALE_40K_HIGHER_GOTHIC:
64
                $handle = fopen($exampleDirectory.'/40k_female_higher_gothic.csv', 'r');
65
                break;
66
            case Type::FEMALE_40K_PRIMITIVE:
67
                $handle = fopen($exampleDirectory.'/40k_female_primitive.csv', 'r');
68
                break;
69
            case Type::MALE_ELVES:
70
                $handle = fopen($exampleDirectory.'/male_elves.csv', 'r');
71
                break;
72
            case Type::FEMALE_ELVES:
73
                $handle = fopen($exampleDirectory.'/female_elves.csv', 'r');
74
                break;
75
            default:
76
                throw new GenerateFromFileException(sprintf('There is no file configured for ID %s', $example));
77
        }
78
79
        while (($data = fgetcsv($handle)) !== false) {
80
            $this->nameList[] = $data[0];
81
        }
82
        fclose($handle);
83
    }
84
85
    public function analyseFromArray(array $nameList): void
86
    {
87
        $this->getParameter()->setLengthsFromNameList($nameList);
88
89
        $this->firstLetters = $this->getFirstLetters($nameList);
90
        $this->possibleNextLetters = $this->getSequences($nameList);
91
    }
92
93
    public function analyseFromJson(string $nameList): void
94
    {
95
        $array = json_decode($nameList, true);
96
97
        $this->analyseFromArray($array);
98
    }
99
100
    public function generate(): string
101
    {
102
        $name = '';
103
        $nextLetter = '';
104
105
        $name.= $this->firstLetters[rand(0, count($this->firstLetters)-1)];
106
107
        while ($nextLetter !== '*' || strlen($name) < 4) {
108
            $nextLetter = $this->possibleNextLetters[strtoupper(substr($name, -1))][rand(0, count($this->possibleNextLetters[strtoupper(substr($name,-1))])-1)];
109
            if ($nextLetter !== '*') {
110
                $name.= strtolower($nextLetter);
111
            }
112
        }
113
114
        try {
115
            IntegrityChecker::check($this->parameter, $name);
116
        } catch (IntegrityException $exception) {
117
            return $this->generate();
118
        }
119
120
        return $name;
121
    }
122
123
    public function getFirstLetters(array $nameList): array
124
    {
125
        asort($nameList);
126
        $stats = [];
127
        $availableLetters = [];
128
129
        foreach ($nameList as $name) {
130
            $firstLetter = strtoupper($name[0]);
131
132
            $stats[$firstLetter] = array_key_exists($firstLetter, $stats) ?
133
                (int) round($stats[$firstLetter]+100/count($nameList)) :
134
                (int) round(100/count($nameList));
135
        }
136
137
        foreach ($stats as $letter => $stat) {
138
            for ($i = 0; $i < $stat; $i++) {
139
                $availableLetters[] = $letter;
140
            }
141
        }
142
143
        return $availableLetters;
144
    }
145
146
    public function getSequences(array $nameList): array
147
    {
148
        asort($nameList);
149
        $stats = [];
150
        $availableLetters = [];
151
152
        foreach ($nameList as $name) {
153
            $length = strlen($name);
154
155
            for ($i = 0; $i < $length; $i++) {
156
                if ($i+1 <= $length) {
157
                    $currentLetter = strtoupper($name[$i]);
158
                    $nextLetter = $i+1 === $length ? '*' : strtolower($name[$i+1]);
159
160
                    if (!array_key_exists($currentLetter, $stats)) {
161
                        $stats[$currentLetter] = [];
162
                    }
163
164
                    $stats[$currentLetter][$nextLetter] = array_key_exists($nextLetter, $stats[$currentLetter]) ?
165
                        $stats[$currentLetter][$nextLetter] + 1 : 1;
166
167
                    ksort($stats[$currentLetter]);
168
                }
169
            }
170
        }
171
        ksort($stats);
172
173
        foreach ($stats as $letter => $stat) {
174
            $total = 0;
175
176
            $availableLetters[$letter] = [];
177
178
            foreach ($stat as $quantity) {
179
                $total = $total + $quantity;
180
            }
181
182
            foreach ($stat as $nextLetter => $quantity) {
183
                $percent = (int) round($quantity * 100 / $total);
184
185
                for ($i = 0; $i < $percent; $i++) {
186
                    $availableLetters[$letter][] = $nextLetter;
187
                }
188
            }
189
        }
190
191
        return $availableLetters;
192
    }
193
194
    public function getParameter(): Parameter
195
    {
196
        return $this->parameter;
197
    }
198
199
    public function setParameter(Parameter $parameter): void
200
    {
201
        $this->parameter = $parameter;
202
    }
203
}