Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.80% |
89 / 91 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
Text | |
97.80% |
89 / 91 |
|
80.00% |
4 / 5 |
35 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
generateText | |
100.00% |
40 / 40 |
|
100.00% |
1 / 1 |
15 | |||
generatePunctuation | |
92.59% |
25 / 27 |
|
0.00% |
0 / 1 |
10.04 | |||
generateParagraph | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
generateFormatting | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Utils\RnG |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Utils\RnG; |
16 | |
17 | /** |
18 | * Text generator. |
19 | * |
20 | * @package phpOMS\Utils\RnG |
21 | * @license OMS License 2.0 |
22 | * @link https://jingga.app |
23 | * @since 1.0.0 |
24 | */ |
25 | class Text |
26 | { |
27 | /** |
28 | * Vocabulary. |
29 | * |
30 | * @var string[] |
31 | * @since 1.0.0 |
32 | */ |
33 | public const LOREM_IPSUM = [ |
34 | 'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'curabitur', 'vel', 'hendrerit', 'libero', |
35 | 'eleifend', 'blandit', 'nunc', 'ornare', 'odio', 'ut', 'orci', 'gravida', 'imperdiet', 'nullam', 'purus', 'lacinia', 'a', |
36 | 'pretium', 'quis', 'congue', 'praesent', 'sagittis', 'laoreet', 'auctor', 'mauris', 'non', 'velit', 'eros', 'dictum', |
37 | 'proin', 'accumsan', 'sapien', 'nec', 'massa', 'volutpat', 'venenatis', 'sed', 'eu', 'molestie', 'lacus', 'quisque', |
38 | 'porttitor', 'ligula', 'dui', 'mollis', 'tempus', 'at', 'magna', 'vestibulum', 'turpis', 'ac', 'diam', 'tincidunt', 'id', |
39 | 'condimentum', 'enim', 'sodales', 'in', 'hac', 'habitasse', 'platea', 'dictumst', 'aenean', 'neque', 'fusce', 'augue', |
40 | 'leo', 'eget', 'semper', 'mattis', 'tortor', 'scelerisque', 'nulla', 'interdum', 'tellus', 'malesuada', 'rhoncus', 'porta', |
41 | 'sem', 'aliquet', 'et', 'nam', 'suspendisse', 'potenti', 'vivamus', 'luctus', 'fringilla', 'erat', 'donec', 'justo', |
42 | 'vehicula', 'ultricies', 'varius', 'ante', 'primis', 'faucibus', 'ultrices', 'posuere', 'cubilia', 'curae', 'etiam', |
43 | 'cursus', 'aliquam', 'quam', 'dapibus', 'nisl', 'feugiat', 'egestas', 'class', 'aptent', 'taciti', 'sociosqu', 'ad', |
44 | 'litora', 'torquent', 'per', 'conubia', 'nostra', 'inceptos', 'himenaeos', 'phasellus', 'nibh', 'pulvinar', 'vitae', |
45 | 'urna', 'iaculis', 'lobortis', 'nisi', 'viverra', 'arcu', 'morbi', 'pellentesque', 'metus', 'commodo', 'ut', 'facilisis', |
46 | 'felis', 'tristique', 'ullamcorper', 'placerat', 'aenean', 'convallis', 'sollicitudin', 'integer', 'rutrum', 'duis', 'est', |
47 | 'etiam', 'bibendum', 'donec', 'pharetra', 'vulputate', 'maecenas', 'mi', 'fermentum', 'consequat', 'suscipit', 'aliquam', |
48 | 'habitant', 'senectus', 'netus', 'fames', 'quisque', 'euismod', 'curabitur', 'lectus', 'elementum', 'tempor', 'risus', |
49 | 'cras', |
50 | ]; |
51 | |
52 | /** |
53 | * Text has random formatting. |
54 | * |
55 | * @var bool |
56 | * @since 1.0.0 |
57 | */ |
58 | public bool $hasFormatting = false; |
59 | |
60 | /** |
61 | * Text has paragraphs. |
62 | * |
63 | * @var bool |
64 | * @since 1.0.0 |
65 | */ |
66 | public bool $hasParagraphs = false; |
67 | |
68 | /** |
69 | * Amount of sentences of the last generated text. |
70 | * |
71 | * @var int |
72 | * @since 1.0.0 |
73 | */ |
74 | public int $sentences = 0; |
75 | |
76 | /** |
77 | * Constructor |
78 | * |
79 | * @param bool $hasFormatting Text should have formatting |
80 | * @param bool $hasParagraphs Text should have paragraphs |
81 | * |
82 | * @since 1.0.0 |
83 | */ |
84 | public function __construct(bool $hasFormatting = false, bool $hasParagraphs = false) |
85 | { |
86 | $this->hasFormatting = $hasFormatting; |
87 | $this->hasParagraphs = $hasParagraphs; |
88 | } |
89 | |
90 | /** |
91 | * Get a random string. |
92 | * |
93 | * @param int $length Text length |
94 | * @param string[] $words Vocabulary |
95 | * |
96 | * @return string |
97 | * |
98 | * @since 1.0.0 |
99 | */ |
100 | public function generateText(int $length, array $words = null) : string |
101 | { |
102 | if ($length === 0) { |
103 | return ''; |
104 | } |
105 | |
106 | if ($words === null) { |
107 | $words = self::LOREM_IPSUM; |
108 | } |
109 | |
110 | $punctuation = $this->generatePunctuation($length); |
111 | $punctuationCount = \array_count_values( |
112 | \array_map( |
113 | function ($item) { |
114 | return $item[1]; |
115 | }, |
116 | $punctuation |
117 | ) |
118 | ) + ['.' => 0, '!' => 0, '?' => 0]; |
119 | |
120 | $this->sentences = $punctuationCount['.'] + $punctuationCount['!'] + $punctuationCount['?']; |
121 | |
122 | if ($this->hasParagraphs) { |
123 | $paragraph = $this->generateParagraph($this->sentences); |
124 | } |
125 | |
126 | if ($this->hasFormatting) { |
127 | $formatting = $this->generateFormatting($length); |
128 | } |
129 | |
130 | $sentenceCount = 0; |
131 | $text = ''; |
132 | $puid = 0; |
133 | $paid = 0; |
134 | $wordCount = \count($words); |
135 | |
136 | for ($i = 0; $i < $length + 1; ++$i) { |
137 | $lastChar = \substr($text, -1); |
138 | $word = $words[\mt_rand(0, $wordCount - 1)] ?? ''; |
139 | |
140 | if ($lastChar === '.' || $lastChar === '!' || $lastChar === '?' || !$lastChar) { |
141 | $word = \ucfirst($word); |
142 | ++$sentenceCount; |
143 | |
144 | /** @noinspection PhpUndefinedVariableInspection */ |
145 | if ($this->hasParagraphs) { |
146 | ++$paid; |
147 | |
148 | $text .= '</p><p>'; |
149 | } |
150 | } |
151 | |
152 | /** @noinspection PhpUndefinedVariableInspection */ |
153 | if ($this->hasFormatting && isset($formatting[$i])) { |
154 | $word = '<' . $formatting[$i] . '>' . $word . '</' . $formatting[$i] . '>'; |
155 | } |
156 | |
157 | $text .= ' ' . $word; |
158 | |
159 | if ($punctuation[$puid][0] === $i) { |
160 | $text .= $punctuation[$puid][1]; |
161 | ++$puid; |
162 | } |
163 | } |
164 | |
165 | $text = \ltrim($text); |
166 | |
167 | return $this->hasParagraphs ? '<p>' . $text . '</p>' : $text; |
168 | } |
169 | |
170 | /** |
171 | * Generate punctuation. |
172 | * |
173 | * @param int $length Text length |
174 | * |
175 | * @return array<int, array<int, int|string>> |
176 | * |
177 | * @since 1.0.0 |
178 | */ |
179 | private function generatePunctuation(int $length) : array |
180 | { |
181 | $minSentences = 4; |
182 | $maxSentences = 20; |
183 | $minCommaSpacing = 3; |
184 | $probComma = 0.2; |
185 | $probDot = 0.8; |
186 | $probExc = 0.4; |
187 | |
188 | $punctuation = []; |
189 | |
190 | for ($i = 0; $i < $length;) { |
191 | $sentenceLength = \mt_rand($minSentences, $maxSentences); |
192 | |
193 | if ($i + $sentenceLength > $length || $length - ($i + $sentenceLength) < $minSentences) { |
194 | $sentenceLength = $length - $i; |
195 | } |
196 | |
197 | /* Handle comma */ |
198 | $posComma = []; |
199 | |
200 | if (\mt_rand(0, 100) <= $probComma * 100 && $sentenceLength >= 2 * $minCommaSpacing) { |
201 | $posComma[] = \mt_rand($minCommaSpacing, $sentenceLength - $minCommaSpacing); |
202 | $punctuation[] = [$i + $posComma[0], ',']; |
203 | |
204 | if (\mt_rand(0, 100) <= $probComma * 100 && $sentenceLength > $posComma[0] + $minCommaSpacing * 2) { |
205 | $posComma[] = \mt_rand($posComma[0] + $minCommaSpacing, $sentenceLength - $minCommaSpacing); |
206 | $punctuation[] = [$i + $posComma[1], ',']; |
207 | } |
208 | } |
209 | |
210 | $i += $sentenceLength; |
211 | |
212 | /* Handle sentence ending */ |
213 | if (\mt_rand(0, 100) <= $probDot * 100) { |
214 | $punctuation[] = [$i, '.']; |
215 | continue; |
216 | } |
217 | |
218 | if (\mt_rand(0, 100) <= $probExc * 100) { |
219 | $punctuation[] = [$i, '!']; |
220 | continue; |
221 | } |
222 | |
223 | $punctuation[] = [$i, '?']; |
224 | } |
225 | |
226 | return $punctuation; |
227 | } |
228 | |
229 | /** |
230 | * Generate paragraphs. |
231 | * |
232 | * @param int $length Amount of sentences |
233 | * |
234 | * @return int[] |
235 | * |
236 | * @since 1.0.0 |
237 | */ |
238 | private function generateParagraph(int $length) : array |
239 | { |
240 | $minSentence = 3; |
241 | $maxSentence = 10; |
242 | |
243 | $paragraph = []; |
244 | |
245 | for ($i = 0; $i < $length;) { |
246 | $paragraphLength = \mt_rand($minSentence, $maxSentence); |
247 | |
248 | if ($i + $paragraphLength > $length || $length - ($i + $paragraphLength) < $minSentence) { |
249 | $paragraphLength = $length - $i; |
250 | } |
251 | |
252 | $i += $paragraphLength; |
253 | $paragraph[] = $i; |
254 | } |
255 | |
256 | return $paragraph; |
257 | } |
258 | |
259 | /** |
260 | * Generate random formatting. |
261 | * |
262 | * @param int $length Amount of words |
263 | * |
264 | * @return string[] |
265 | * |
266 | * @since 1.0.0 |
267 | */ |
268 | private function generateFormatting(int $length) : array |
269 | { |
270 | $probCursive = 0.005; |
271 | $probBold = 0.005; |
272 | $probUline = 0.005; |
273 | |
274 | $formatting = []; |
275 | |
276 | for ($i = 0; $i < $length; ++$i) { |
277 | if (\mt_rand(0, 1000) <= 1000 * $probUline) { |
278 | $formatting[$i] = 'u'; |
279 | } |
280 | |
281 | if (\mt_rand(0, 1000) <= 1000 * $probBold) { |
282 | $formatting[$i] = 'b'; |
283 | } |
284 | |
285 | if (\mt_rand(0, 1000) <= 1000 * $probCursive) { |
286 | $formatting[$i] = 'i'; |
287 | } |
288 | } |
289 | |
290 | return $formatting; |
291 | } |
292 | } |