packages/sql-faker/src/MySql/Generation/Value/LiteralGenerator.php
1<?php
2
3declare(strict_types=1);
4
5namespace SqlFaker\MySql\Generation\Value;
6
7use Faker\Generator as FakerGenerator;
8use LogicException;
9use SqlFaker\Generation\Value\RandomCharacters;
10
11/**
12 * Generates MySql literal bodies for the bounded lexical API.
13 *
14 * Owns the alphabets and numeric formatting used by this dialect.
15 *
16 * @visibility root
17 */
18final class LiteralGenerator
19{
20 private const ALPHA_UNDERSCORE = 'abcdefghijklmnopqrstuvwxyz_';
21 private const ALNUM_UNDERSCORE = 'abcdefghijklmnopqrstuvwxyz0123456789_';
22 private const MIXED_ALNUM_UNDERSCORE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
23 private const HOSTNAME_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789';
24 private const HEX_CHARS = '0123456789abcdef';
25 private const DIGIT_CHARS = '0123456789';
26 private const BINARY_CHARS = '01';
27
28 private FakerGenerator $faker;
29
30 private RandomCharacters $characters;
31
32 /**
33 * @param FakerGenerator $faker Source of every choice the generated strings make
34 */
35 public function __construct(FakerGenerator $faker)
36 {
37 $this->faker = $faker;
38 $this->characters = new RandomCharacters($faker);
39 }
40
41 /**
42 * Generate a raw SQL identifier (starts with letter or underscore, followed by alphanumeric/underscore).
43 */
44 public function rawIdentifier(int $minLength = 1, int $maxLength = 12): string
45 {
46 $length = $this->faker->numberBetween(max($minLength, 1), $maxLength);
47
48 return $this->characters->character(self::ALPHA_UNDERSCORE)
49 . $this->characters->string(self::ALNUM_UNDERSCORE, $length - 1);
50 }
51
52 /**
53 * Generate a random string from the mixed alphanumeric+underscore alphabet.
54 */
55 public function mixedAlnumString(int $minLength = 1, int $maxLength = 24): string
56 {
57 return $this->characters->string(self::MIXED_ALNUM_UNDERSCORE, $this->faker->numberBetween($minLength, $maxLength));
58 }
59
60 /**
61 * Generate a random integer literal string.
62 *
63 * @return non-empty-string
64 */
65 public function integerString(int $min = 1, int $max = 9999999999): string
66 {
67 return (string) $this->faker->numberBetween($min, $max);
68 }
69
70 /**
71 * Generate a random hexadecimal string.
72 */
73 public function hexString(int $minLength = 1, int $maxLength = 16): string
74 {
75 return $this->characters->string(self::HEX_CHARS, $this->faker->numberBetween($minLength, $maxLength));
76 }
77
78 /**
79 * Generate a random binary string (0s and 1s).
80 */
81 public function binaryString(int $minLength = 1, int $maxLength = 32): string
82 {
83 return $this->characters->string(self::BINARY_CHARS, $this->faker->numberBetween($minLength, $maxLength));
84 }
85
86 /**
87 * Generate a random decimal literal string.
88 *
89 * @return non-empty-string
90 */
91 public function decimalString(int $precision = 13, int $scale = 6): string
92 {
93 $intDigits = max($precision - $scale, 1);
94 $maxIntPart = (int) str_repeat('9', $intDigits);
95 $maxFracPart = (int) str_repeat('9', max($scale, 2));
96
97 $intPart = $this->faker->numberBetween(0, $maxIntPart);
98 $fracPart = $this->faker->numberBetween(0, $maxFracPart);
99
100 return $intPart . '.' . str_pad((string) $fracPart, max($scale, 2), '0', STR_PAD_LEFT);
101 }
102
103 /**
104 * Generate a random hostname string.
105 *
106 * @return non-empty-string
107 *
108 * @throws LogicException When the requested bounds leave every part of the hostname empty
109 */
110 public function hostnameString(int $minParts = 1, int $maxParts = 3, int $minPartLength = 1, int $maxPartLength = 12): string
111 {
112 $parts = $this->faker->numberBetween($minParts, $maxParts);
113 $segments = [];
114 for ($p = 0; $p < $parts; $p++) {
115 $segments[] = $this->characters->string(self::HOSTNAME_CHARS, $this->faker->numberBetween($minPartLength, $maxPartLength));
116 }
117
118 $hostname = implode('.', $segments);
119 if ($hostname === '') {
120 throw new LogicException('Hostname generation produced an empty result.');
121 }
122
123 return $hostname;
124 }
125
126 /**
127 * Generate a random unsigned big integer string.
128 *
129 * @return non-empty-string
130 */
131 public function unsignedBigIntString(int $minLength = 1, int $maxLength = 20): string
132 {
133 $buf = $this->characters->string(self::DIGIT_CHARS, $this->faker->numberBetween($minLength, $maxLength));
134 $trimmed = ltrim($buf, '0');
135
136 return $trimmed === '' ? '0' : $trimmed;
137 }
138
139 /**
140 * Generate a random long integer string.
141 *
142 * @return non-empty-string
143 */
144 public function longIntString(int $min = 0, int $max = 2147483647): string
145 {
146 $val = $this->faker->numberBetween($min, $max);
147
148 return "{$val}";
149 }
150
151 /**
152 * Generate a random float literal string with exponent.
153 *
154 * @return non-empty-string
155 */
156 public function floatString(string $mantissa, int $minExponent = -20, int $maxExponent = 20): string
157 {
158 return $mantissa . 'e' . $this->faker->numberBetween($minExponent, $maxExponent);
159 }
160
161
162
163
164
165}
166