RandomEnglishGenerator::chooseRandomWord()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.536
c 0
b 0
f 0
cc 1
nc 1
nop 4
1
<?php declare(strict_types = 1);
2
3
namespace Suilven\RandomEnglish;
4
5
use Faker\Factory;
6
use Suilven\RandomEnglish\Helper\LanguageHelper;
7
8
class RandomEnglishGenerator
9
{
10
11
    private const POSSIBLE_WORD_TYPES = [
12
        'noun',
13
        'countable_noun',
14
15
        'adjective',
16
        'preposition',
17
        'conjunction',
18
        'contraction',
19
        'control_verb',
20
21
        'sequence_adverb',
22
        'frequency_adverb',
23
24
        // pluralize and ing these?
25
        'intransitive_verb',
26
        'irregular_verb',
27
        'transitive_verb',
28
        'verb',
29
        'adverb',
30
31
        'intransitive_verb_ing',
32
        'irregular_verb_ing',
33
        'transitive_verb_ing',
34
        'verb_ing',
35
36
37
        // positions
38
        'locative',
39
40
        'plural_noun',
41
42
43
        // names
44
        'mens_name',
45
        'womens_name',
46
        'country',
47
        'colour',
48
    ];
49
50
    private const POSSIBLE_FAKER_TYPES=[
51
        'address',
52
        'name',
53
        'randomDigit',
54
        'randomLetter',
55
        'randomNumber',
56
        'title',
57
        'titleMale',
58
        'titleFemale',
59
        'firstNameMale',
60
        'firstNameFemale',
61
        'lastName',
62
63
        'catchPhrase',
64
        'bs',
65
        'company',
66
        'companySuffix',
67
        'jobTitle',
68
69
        'realText',
70
71
        'dayOfWeek',
72
        'monthName',
73
74
    ];
75
76
    /** @var string configuration file for sentences */
77
    private $config = '';
78
79
    public function __construct()
80
    {
81
        $configFile = \dirname(__FILE__) . '/../config/sentence-structure.cfg';
82
        $this->setConfig(\file_get_contents($configFile));
83
    }
84
85
86
    /** @param string $newConfig configuration file of how sentence structures */
87
    public function setConfig(string $newConfig): void
88
    {
89
        $this->config = \trim($newConfig);
90
    }
91
92
93
    /**
94
     * Generate a random sentence
95
     *
96
     * @param bool $titleCase true to generate a title, false (default) to generate a sentence
97
     * @return string a random sentence
98
     */
99
    public function sentence(bool $titleCase = false): string
100
    {
101
        // choose a random sentence structure
102
        $structures = \explode(\PHP_EOL, $this->config);
103
        \shuffle($structures);
104
        $structure = $structures[0];
105
106
        $expression = '/[\s]+/';
107
        $splits = \preg_split($expression, $structure, -1, \PREG_SPLIT_NO_EMPTY);
108
109
        $sentenceArray = [];
110
111
        /** @var string $possiblyRandomWord */
112
        foreach ($splits as $possiblyRandomWord) {
113
            $word = $this->getWordOrRandomWord($possiblyRandomWord);
114
            $sentenceArray[] = $word;
115
        }
116
117
        $result = $this->makeArrayIntoSentence($sentenceArray);
118
        $this->augmentSentenceTitleCase($titleCase, $result);
119
120
        return $result;
121
    }
122
123
124
    /**
125
     * Generate a random title
126
     *
127
     * @return string a random sentence, all in caps, no trailing full stop
128
     */
129
    public function title(): string
130
    {
131
        return $this->sentence(true);
132
    }
133
134
135
    /**
136
     * @param int $maxSentences The maximum number of sentences
137
     * @return string a paragraph of random sentences
138
     */
139
    public function paragraph(int $maxSentences = 10): string
140
    {
141
        $nParagraph = \rand(1, $maxSentences);
142
        $sentences = [];
143
144
        for ($i = 0; $i < $nParagraph; $i++) {
145
            $sentences[] = $this->sentence();
146
        }
147
148
        return \implode("  ", $sentences);
149
    }
150
151
152
    /** @return string a plural noun */
153
    public function pluralNoun(): string
154
    {
155
        $plurableNoun = $this->getRandomWord('countable_noun');
156
157
        return $plurableNoun . 's';
158
    }
159
160
161
    /** @return string the ing version of a verb, e.g. run -> running */
162
    public function verbing(): string
163
    {
164
        // @todo Choose a random verb source file
165
        $verb = $this->getRandomWord('verb');
166
        $helper = new LanguageHelper();
167
168
        return $helper->ingVerb($verb);
169
    }
170
171
172
    /**
173
     * @param string $wordType The word type, e.g. 'noun' or 'verb'
174
     * @return bool true if this is a plural noun
175
     */
176
    public function pluralNounCheck(string &$wordType, string &$possiblyRandomWord): bool
177
    {
178
        $pluralNoun = ($wordType === 'plural_noun');
179
        if ($pluralNoun) {
180
            $wordType = 'noun';
181
            $possiblyRandomWord = \str_replace('plural_', '', $possiblyRandomWord);
182
        }
183
184
        return $pluralNoun;
185
    }
186
187
188
    /** @return bool true if this is a verb with ing */
189
    public function ingVerbCheck(string &$wordType, string &$possiblyRandomWord): bool
190
    {
191
        $ing = \substr($wordType, -4) === '_ing';
192
        if ($ing) {
193
            $wordType = \str_replace('_ing', '', $wordType);
194
            $possiblyRandomWord = \str_replace('_ing', '', $possiblyRandomWord);
195
        }
196
197
        return $ing;
198
    }
199
200
201
    public function augmentIfPluralNoun(bool $pluralNoun, string &$randomWord): void
202
    {
203
        if (!$pluralNoun) {
204
            return;
205
        }
206
207
        $randomWord = $this->pluralNoun();
208
    }
209
210
211
    public function augmentIfIngVerb(bool $ing, string &$randomWord): void
212
    {
213
        if (!$ing) {
214
            return;
215
        }
216
217
        $randomWord = $this->verbing();
218
    }
219
220
221
    public function augmentSentenceTitleCase(bool $titleCase, string &$result): void
222
    {
223
        if (!$titleCase) {
224
            return;
225
        }
226
227
        $result = \ucwords($result);
228
        $result = \substr_replace($result, "", -1);
229
    }
230
231
232
    /**
233
     * Convert an array of strings into a sentence
234
     * 1) Capitalize first letter
235
     * 2) Glue words together with a space
236
     * 3) Add a full stop
237
     * 4) Deal with diferent sentence endings such as ! or ?
238
     *
239
     * @param array<string> $sentenceArray a sentence whose words are in an array of strings
240
     * @return string a sentence
241
     */
242
    public function makeArrayIntoSentence(array $sentenceArray): string
243
    {
244
        // ensure sentence starts with a capital
245
        $sentenceArray[0] = \ucfirst($sentenceArray[0]);
246
247
        $sentence = \implode(" ", $sentenceArray) . '.';
248
        $this->fixEndOfSentence($sentence);
249
250
        return $sentence;
251
    }
252
253
254
    /**
255
     * If a sentence ended with a question or exclamation mark prior to having a full stop appended, deal with it
256
     *
257
     * @param string $result The sentence, by reference, prior to being fixed
258
     */
259
    public function fixEndOfSentence(string &$result): void
260
    {
261
        $result = \str_replace('?.', '?', $result);
262
        $result = \str_replace('!.', '?', $result);
263
        $result = \str_replace('..', '.', $result);
264
    }
265
266
267
    /**
268
     * @param string $wordType the type of word, e.g. noun, verb
269
     * @return string a random word for the given word type
270
     */
271
    private function getRandomWord(string $wordType): string
272
    {
273
        $wordsFile = \dirname(__FILE__) . '/../words/english_' . $wordType . 's.txt';
274
        $words = \explode(\PHP_EOL, \trim(\file_get_contents($wordsFile)));
275
        \shuffle($words);
276
277
        return $words[0];
278
    }
279
280
281
    /** @return string the above string as is, or a possibly randomized version, e.g. a random noun */
282
    private function getWordOrRandomWord(string $possiblyRandomWord): string
283
    {
284
        $result = $possiblyRandomWord;
285
286
        if (\strstr($possiblyRandomWord, '|')) {
287
            $splits = \explode('|', $possiblyRandomWord);
288
            \shuffle($splits);
289
            $result = $splits[0];
290
        } else {
291
            foreach (self::POSSIBLE_WORD_TYPES as $wordType) {
292
                $pluralNoun = $this->pluralNounCheck($wordType, $possiblyRandomWord);
293
                $ing = $this->ingVerbCheck($wordType, $possiblyRandomWord);
294
295
                $start = '[' . $wordType . ']';
296
297
                // check the start of the word as it may be suffixed by likes of a question of exclamation mark.
298
                // If no match for the possible word type continue until the next one
299
                if (\substr($possiblyRandomWord, 0, \strlen($start)) !== $start) {
300
                    continue;
301
                }
302
303
                // ---- we are now getting a random word from a file ----
304
                $result = $this->chooseRandomWord($possiblyRandomWord, $wordType, $pluralNoun, $ing);
305
306
                break;
307
            }
308
309
            $faker = Factory::create();
310
            foreach (self::POSSIBLE_FAKER_TYPES as $wordType) {
311
                $start = '[' . $wordType . ']';
312
313
                // check the start of the word as it may be suffixed by likes of a question of exclamation mark.
314
                // If no match for the possible word type continue until the next one
315
                if (\substr($possiblyRandomWord, 0, \strlen($start)) !== $start) {
316
                    continue;
317
                }
318
319
                $result = $faker->$wordType;
320
            }
321
        }
322
323
        // no randomized word has been found, aka no [verb] or [noun], thus append the possibly random word as is
324
        return $result;
325
    }
326
327
328
    /**
329
     * @param string $wordType noun, verb, adjective etc
330
     * @param bool $pluralizeNoun if true pluralize a noun
331
     * @param bool $makeVerbIng if true make a verb an ing verb, e.g. run -> running
332
     */
333
    private function chooseRandomWord(
334
        string $randomWordTypeWithSuffix,
335
        string $wordType,
336
        bool $pluralizeNoun,
337
        bool $makeVerbIng
338
    ): string {
339
        $start = '[' . $wordType . ']';
340
341
        // note the suffix of the word, and append this to the random word
342
        $restOfWord = \str_replace($start, '', $randomWordTypeWithSuffix);
343
344
        // This avoids having to use 'countrie' in the sentence structure file : country -> countries
345
        $wordType = \str_replace('country', 'countrie', $wordType);
346
347
        /** @var string $randomWord */
348
        $randomWord = $this->getRandomWord($wordType);
349
350
        // augment the random word if a plural noun or an ing verb
351
        $this->augmentIfPluralNoun($pluralizeNoun, $randomWord);
352
        $this->augmentIfIngVerb($makeVerbIng, $randomWord);
353
354
        // add the random word, and then the suffix of the word such as ?! or !!
355
        return $randomWord . $restOfWord;
356
    }
357
}
358