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 | } |