1use mago_interner::ThreadedInterner;
2use mago_names::ResolvedNames;
3use mago_reflection::CodebaseReflection;
4use mago_reflection::r#type::kind::*;
5use mago_syntax::ast::Identifier;
6use mago_trinary::Trinary;
7use ordered_float::OrderedFloat;
8
9pub struct ConstantTypeResolver<'i, 'c> {
10 interner: &'i ThreadedInterner,
11 names: &'c ResolvedNames,
12 codebase: Option<&'c CodebaseReflection>,
13}
14
15impl<'i, 'c> ConstantTypeResolver<'i, 'c> {
16 pub fn new(
17 interner: &'i ThreadedInterner,
18 names: &'c ResolvedNames,
19 codebase: Option<&'c CodebaseReflection>,
20 ) -> Self {
21 Self { interner, names, codebase }
22 }
23
24 pub fn resolve(&self, constant: &Identifier) -> TypeKind {
25 let (short_name, full_name) = if self.names.is_imported(constant) {
26 let name = self.interner.lookup(self.names.get(constant));
27
28 (name, name)
29 } else {
30 let short_name = self.interner.lookup(constant.value());
31 let imported_name = self.interner.lookup(self.names.get(constant));
32
33 if let Some(stripped) = short_name.strip_prefix('\\') {
34 (stripped, imported_name)
35 } else {
36 (short_name, imported_name)
37 }
38 };
39
40 match short_name {
41 "PHP_VERSION" => non_empty_string_kind(),
42 "PHP_MAJOR_VERSION" => integer_range_kind(7, 9),
43 "PHP_MINOR_VERSION" => non_negative_integer_kind(),
44 "PHP_RELEASE_VERSION" => non_negative_integer_kind(),
45 "PHP_VERSION_ID" => integer_range_kind(70000, 99999),
46 "PHP_ZTS" => union_kind(vec![value_integer_kind(0), value_integer_kind(1)]),
47 "PHP_DEBUG" => union_kind(vec![value_integer_kind(0), value_integer_kind(1)]),
48 "PHP_MAXPATHLEN" => positive_integer_kind(),
49 "PHP_OS" => non_empty_string_kind(),
50 "PHP_OS_FAMILY" => union_kind(vec![
51 value_string_kind(
52 self.interner.intern("Windows"),
53 7,
54 Trinary::False,
55 Trinary::False,
56 Trinary::False,
57 Trinary::False,
58 ),
59 value_string_kind(
60 self.interner.intern("BSD"),
61 3,
62 Trinary::True,
63 Trinary::True,
64 Trinary::False,
65 Trinary::False,
66 ),
67 value_string_kind(
68 self.interner.intern("Darwin"),
69 6,
70 Trinary::False,
71 Trinary::False,
72 Trinary::False,
73 Trinary::False,
74 ),
75 value_string_kind(
76 self.interner.intern("Linux"),
77 5,
78 Trinary::False,
79 Trinary::False,
80 Trinary::False,
81 Trinary::False,
82 ),
83 value_string_kind(
84 self.interner.intern("Solaris"),
85 7,
86 Trinary::False,
87 Trinary::False,
88 Trinary::False,
89 Trinary::False,
90 ),
91 value_string_kind(
92 self.interner.intern("Unknown"),
93 7,
94 Trinary::False,
95 Trinary::False,
96 Trinary::False,
97 Trinary::False,
98 ),
99 ]),
100 "PHP_SAPI" => union_kind(vec![
101 value_string_kind(
102 self.interner.intern("apache"),
103 6,
104 Trinary::False,
105 Trinary::False,
106 Trinary::True,
107 Trinary::True,
108 ),
109 value_string_kind(
110 self.interner.intern("apache2handler"),
111 14,
112 Trinary::False,
113 Trinary::False,
114 Trinary::True,
115 Trinary::True,
116 ),
117 value_string_kind(
118 self.interner.intern("cgi"),
119 3,
120 Trinary::False,
121 Trinary::False,
122 Trinary::True,
123 Trinary::True,
124 ),
125 value_string_kind(
126 self.interner.intern("cli"),
127 3,
128 Trinary::False,
129 Trinary::False,
130 Trinary::True,
131 Trinary::True,
132 ),
133 value_string_kind(
134 self.interner.intern("cli-server"),
135 10,
136 Trinary::False,
137 Trinary::False,
138 Trinary::True,
139 Trinary::True,
140 ),
141 value_string_kind(
142 self.interner.intern("embed"),
143 5,
144 Trinary::False,
145 Trinary::False,
146 Trinary::True,
147 Trinary::True,
148 ),
149 value_string_kind(
150 self.interner.intern("fpm-fcgi"),
151 8,
152 Trinary::False,
153 Trinary::False,
154 Trinary::True,
155 Trinary::True,
156 ),
157 value_string_kind(
158 self.interner.intern("litespeed"),
159 9,
160 Trinary::False,
161 Trinary::False,
162 Trinary::True,
163 Trinary::True,
164 ),
165 value_string_kind(
166 self.interner.intern("phpdbg"),
167 6,
168 Trinary::False,
169 Trinary::False,
170 Trinary::True,
171 Trinary::True,
172 ),
173 non_empty_string_kind(),
174 ]),
175 "PHP_EOL" => union_kind(vec![
176 value_string_kind(
177 self.interner.intern("\n"),
178 1,
179 Trinary::False,
180 Trinary::False,
181 Trinary::False,
182 Trinary::False,
183 ),
184 value_string_kind(
185 self.interner.intern("\r\n"),
186 2,
187 Trinary::False,
188 Trinary::False,
189 Trinary::False,
190 Trinary::False,
191 ),
192 ]),
193 "PHP_INT_MAX" => union_kind(vec![value_integer_kind(9223372036854775807), value_integer_kind(2147483647)]),
194 "PHP_INT_MIN" => {
195 union_kind(vec![value_integer_kind(-9223372036854775808), value_integer_kind(-2147483648)])
196 }
197 "PHP_INT_SIZE" => union_kind(vec![value_integer_kind(4), value_integer_kind(8)]),
198 "PHP_FLOAT_DIG" => positive_integer_kind(),
199 "PHP_FLOAT_EPSILON" => union_kind(vec![
200 value_float_kind(OrderedFloat(2.220_446_049_250_313e-16)),
201 value_float_kind(OrderedFloat(1.19209290e-7)),
202 ]),
203 "PHP_EXTENSION_DIR" => non_empty_string_kind(),
204 "PHP_PREFIX" => non_empty_string_kind(),
205 "PHP_BINDIR" => non_empty_string_kind(),
206 "PHP_BINARY" => non_empty_string_kind(),
207 "PHP_MANDIR" => non_empty_string_kind(),
208 "PHP_LIBDIR" => non_empty_string_kind(),
209 "PHP_DATADIR" => non_empty_string_kind(),
210 "PHP_SYSCONFDIR" => non_empty_string_kind(),
211 "PHP_LOCALSTATEDIR" => non_empty_string_kind(),
212 "PHP_CONFIG_FILE_PATH" => non_empty_string_kind(),
213 "PHP_SHLIB_SUFFIX" => union_kind(vec![
214 value_string_kind(
215 self.interner.intern("so"),
216 2,
217 Trinary::False,
218 Trinary::False,
219 Trinary::True,
220 Trinary::True,
221 ),
222 value_string_kind(
223 self.interner.intern("dll"),
224 3,
225 Trinary::False,
226 Trinary::False,
227 Trinary::True,
228 Trinary::True,
229 ),
230 ]),
231 "PHP_FD_SETSIZE" => positive_integer_kind(),
232 "PHP_WINDOWS_VERSION_MAJOR" => union_kind(vec![
233 value_integer_kind(4), value_integer_kind(5), value_integer_kind(6), ]),
237 "PHP_WINDOWS_VERSION_MINOR" => union_kind(vec![
238 value_integer_kind(0), value_integer_kind(1), value_integer_kind(2), value_integer_kind(10), value_integer_kind(90), ]),
244 "PHP_WINDOWS_VERSION_BUILD" => positive_integer_kind(),
245 "DIRECTORY_SEPARATOR" => union_kind(vec![
246 value_string_kind(
247 self.interner.intern("/"),
248 1,
249 Trinary::False,
250 Trinary::False,
251 Trinary::False,
252 Trinary::False,
253 ),
254 value_string_kind(
255 self.interner.intern("\\"),
256 1,
257 Trinary::False,
258 Trinary::False,
259 Trinary::False,
260 Trinary::False,
261 ),
262 ]),
263 "PATH_SEPARATOR" => union_kind(vec![
264 value_string_kind(
265 self.interner.intern(";"),
266 1,
267 Trinary::False,
268 Trinary::False,
269 Trinary::False,
270 Trinary::False,
271 ),
272 value_string_kind(
273 self.interner.intern(":"),
274 1,
275 Trinary::False,
276 Trinary::False,
277 Trinary::False,
278 Trinary::False,
279 ),
280 ]),
281 "ICONV_IMPL" => non_empty_string_kind(),
282 "LIBXML_VERSION" => positive_integer_kind(),
283 "LIBXML_DOTTED_VERSION" => non_empty_string_kind(),
284 "OPENSSL_VERSION_NUMBER" => positive_integer_kind(),
285 "PCRE_VERSION" => non_empty_string_kind(),
286 "STDIN" | "STDOUT" | "STDERR" => resource_kind(),
287 "NAN" => value_float_kind(OrderedFloat(f64::NAN)),
288 "INF" => value_float_kind(OrderedFloat(f64::INFINITY)),
289 _ => {
290 if let Some(codebase) = self.codebase {
291 let short_name_id = self.interner.intern(short_name);
292 let full_name_id = self.interner.intern(full_name);
293
294 let Some(constant) = codebase
295 .get_constant(self.interner, &full_name_id)
296 .or_else(|| codebase.get_constant(self.interner, &short_name_id))
297 else {
298 return mixed_kind(false);
299 };
300
301 constant.type_reflection.kind.clone()
302 } else {
303 mixed_kind(false)
304 }
305 }
306 }
307 }
308}